Permalink
Browse files

Merge pull request #174 from KDAB/inspector-for-upstream

  • Loading branch information...
2 parents f77046e + fbdbcd7 commit 61f44342471a4ac49ab8280ab61b92e17a74e76c @ariya ariya committed Nov 17, 2011
Showing with 243 additions and 39 deletions.
  1. +27 −0 src/config.cpp
  2. +7 −0 src/config.h
  3. +8 −0 src/debug_harness.html
  4. +12 −0 src/debug_wrapper.js
  5. +51 −9 src/phantom.cpp
  6. +7 −0 src/phantom.h
  7. +2 −0 src/phantomjs.qrc
  8. +1 −0 src/usage.txt
  9. +98 −30 src/utils.cpp
  10. +10 −0 src/utils.h
  11. +16 −0 src/webpage.cpp
  12. +4 −0 src/webpage.h
View
@@ -126,6 +126,11 @@ void Config::processArgs(const QStringList &args)
loadJsonFile(configPath);
continue;
}
+ if (arg.startsWith("--remote-debugger-port=")) {
+ setDebug(true);
+ setRemoteDebugPort(arg.mid(23).trimmed().toInt());
+ continue;
+ }
if (arg.startsWith("--")) {
setUnknownOption(QString("Unknown option '%1'").arg(arg));
return;
@@ -355,6 +360,26 @@ void Config::setVersionFlag(const bool value)
m_versionFlag = value;
}
+bool Config::debug() const
+{
+ return m_debug;
+}
+
+void Config::setDebug(const bool value)
+{
+ m_debug = value;
+}
+
+int Config::remoteDebugPort() const
+{
+ return m_remoteDebugPort;
+}
+
+void Config::setRemoteDebugPort(const int port)
+{
+ m_remoteDebugPort = port;
+}
+
// private:
void Config::resetToDefaults()
{
@@ -373,6 +398,8 @@ void Config::resetToDefaults()
m_scriptFile.clear();
m_unknownOption.clear();
m_versionFlag = false;
+ m_debug = false;
+ m_remoteDebugPort = -1;
}
void Config::setProxyHost(const QString &value)
View
@@ -98,6 +98,11 @@ class Config: QObject
bool versionFlag() const;
void setVersionFlag(const bool value);
+ void setDebug(const bool value);
+ bool debug() const;
+
+ void setRemoteDebugPort(const int port);
+ int remoteDebugPort() const;
private:
void resetToDefaults();
void setProxyHost(const QString &value);
@@ -122,6 +127,8 @@ class Config: QObject
bool m_versionFlag;
QString m_authUser;
QString m_authPass;
+ bool m_debug;
+ int m_remoteDebugPort;
};
#endif // CONFIG_H
View
@@ -0,0 +1,8 @@
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<script src="%1" type="text/javascript"></script>
+</head>
+<body>
+</body>
+</html>
View
@@ -0,0 +1,12 @@
+// This is a wrapper for the script to be executed.
+// When remote debugging, there's no way to reload the page
+// to be able to run the script being debugged, because there's
+// no user-facing way to interact with phantomjs.
+// This provides a function wrapper around the script in order to
+// make the whole script callable from the console on the remote debugger.
+
+var __run = function() {
+
+%1
+
+};
View
@@ -93,6 +93,8 @@ Phantom::Phantom(QObject *parent)
connect(m_page, SIGNAL(javaScriptConsoleMessageSent(QString, int, QString)),
SLOT(printConsoleMessage(QString, int, QString)));
+ connect(m_page, SIGNAL(initialized()),
+ SLOT(onInitialized()));
m_defaultPageSettings[PAGE_SETTINGS_LOAD_IMAGES] = QVariant::fromValue(m_config.autoLoadImages());
m_defaultPageSettings[PAGE_SETTINGS_LOAD_PLUGINS] = QVariant::fromValue(m_config.pluginsEnabled());
@@ -104,13 +106,14 @@ Phantom::Phantom(QObject *parent)
setLibraryPath(QFileInfo(m_config.scriptFile()).dir().absolutePath());
- // Add 'phantom' object to the global scope
- m_page->mainFrame()->addToJavaScriptWindowObject("phantom", this);
+ onInitialized();
+}
- // Bootstrap the PhantomJS scope
- m_page->mainFrame()->evaluateJavaScript(Utils::readResourceFileUtf8(":/bootstrap.js"));
+Phantom::~Phantom()
+{
}
+
QStringList Phantom::args() const
{
return m_config.scriptArgs();
@@ -139,9 +142,18 @@ bool Phantom::execute()
if (m_config.scriptFile().isEmpty())
return false;
- if (!Utils::injectJsInFrame(m_config.scriptFile(), m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), true)) {
- m_returnValue = -1;
- return false;
+ if (m_config.debug())
+ {
+ if (!Utils::loadJSForDebug(m_config.scriptFile(), m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), true)) {
+ m_returnValue = -1;
+ return false;
+ }
+ m_page->showInspector(m_config.remoteDebugPort());
+ } else {
+ if (!Utils::injectJsInFrame(m_config.scriptFile(), m_scriptFileEnc, QDir::currentPath(), m_page->mainFrame(), true)) {
+ m_returnValue = -1;
+ return false;
+ }
}
return !m_terminated;
@@ -221,16 +233,46 @@ bool Phantom::injectJs(const QString &jsFilePath)
void Phantom::exit(int code)
{
+ if (m_config.debug())
+ Terminal::instance()->cout("Phantom::exit() called but not quitting in debug mode.");
+ else {
+ doExit(0);
+ }
+}
+
+void Phantom::debugExit(int code)
+{
+ doExit(code);
+}
+
+
+void Phantom::doExit(int code)
+{
+ if (m_config.debug())
+ {
+ Utils::cleanupFromDebug();
+ }
+
m_terminated = true;
m_returnValue = code;
qDeleteAll(m_pages);
m_pages.clear();
m_page = 0;
- qDeleteAll(m_servers);
- m_servers.clear();
QApplication::instance()->exit(code);
}
+
+void
+Phantom::onInitialized()
+{
+ // Add 'phantom' object to the global scope
+ m_page->mainFrame()->addToJavaScriptWindowObject("phantom", this);
+
+ // Bootstrap the PhantomJS scope
+ m_page->mainFrame()->evaluateJavaScript(Utils::readResourceFileUtf8(":/bootstrap.js"));
+}
+
+
// private slots:
void Phantom::printConsoleMessage(const QString &message, int lineNumber, const QString &source)
{
View
@@ -52,6 +52,7 @@ class Phantom: public QObject
public:
Phantom(QObject *parent = 0);
+ virtual ~Phantom();
QStringList args() const;
@@ -76,12 +77,18 @@ public slots:
QObject *createFilesystem();
QString loadModuleSource(const QString &name);
bool injectJs(const QString &jsFilePath);
+
+ // exit() will not exit in debug mode. debugExit() will always exit.
void exit(int code = 0);
+ void debugExit(int code = 0);
private slots:
void printConsoleMessage(const QString &msg, int lineNumber, const QString &source);
+ void onInitialized();
private:
+ void doExit(int code);
+
Encoding m_scriptFileEnc;
WebPage *m_page;
bool m_terminated;
View
@@ -1,5 +1,7 @@
<RCC>
<qresource prefix="/">
+ <file>debug_harness.html</file>
+ <file>debug_wrapper.js</file>
<file>phantomjs-icon.png</file>
<file>coffee-script.js</file>
<file>usage.txt</file>
View
@@ -12,6 +12,7 @@ Options:
--local-to-remote-url-access=[yes|no] Local content can access remote URL (default is 'no')
--max-disk-cache-size=size Limits the size of disk cache (in KB)
--output-encoding Sets the encoding used for terminal output (default is 'utf8')
+--remote-debugger-port=port Starts the script in a debug harness and listens on the desired port.
--proxy=address:port Sets the network proxy (e.g. "--proxy=192.168.1.42:8080")
--script-encoding Sets the encoding used for the starting script (default is 'utf8')
--version Prints out PhantomJS version
View
@@ -32,11 +32,15 @@
#include <QDebug>
#include <QDateTime>
#include <QDir>
+#include <QTemporaryFile>
#include "consts.h"
#include "terminal.h"
#include "utils.h"
+QTemporaryFile* Utils::m_tempHarness = 0;
+QTemporaryFile* Utils::m_tempWrapper = 0;
+
// public:
void Utils::showUsage()
{
@@ -76,53 +80,117 @@ bool Utils::injectJsInFrame(const QString &jsFilePath, const QString &libraryPat
bool Utils::injectJsInFrame(const QString &jsFilePath, const Encoding &jsFileEnc, const QString &libraryPath, QWebFrame *targetFrame, const bool startingScript)
{
// Don't do anything if an empty string is passed
+ QString scriptPath = findScript(jsFilePath, libraryPath);
+ QString scriptBody = jsFromScriptFile(scriptPath, jsFileEnc);
+ if (scriptBody.isEmpty())
+ {
+ if (startingScript) {
+ Terminal::instance()->cerr(QString("Can't open '%1'").arg(jsFilePath));
+ } else {
+ qWarning("Can't open '%s'", qPrintable(jsFilePath));
+ }
+ return false;
+ }
+ // Execute JS code in the context of the document
+ targetFrame->evaluateJavaScript(scriptBody);
+ return true;
+}
+
+bool Utils::loadJSForDebug(const QString& jsFilePath, const QString& libraryPath, QWebFrame* targetFrame, const bool startingScript)
+{
+ return loadJSForDebug(jsFilePath, Encoding::UTF8, libraryPath, targetFrame, startingScript);
+}
+
+bool Utils::loadJSForDebug(const QString& jsFilePath, const Encoding& jsFileEnc, const QString& libraryPath, QWebFrame* targetFrame, const bool startingScript)
+{
+
+ QString scriptPath = findScript(jsFilePath, libraryPath);
+ QString scriptBody = jsFromScriptFile(scriptPath, jsFileEnc);
+
+ QFile wrapper(":/debug_wrapper.js");
+ if (!wrapper.open(QIODevice::ReadOnly))
+ return false; // We got big issues
+ QString jsWrapper = QString::fromUtf8(wrapper.readAll());
+ jsWrapper = jsWrapper.arg(scriptBody);
+ m_tempWrapper = new QTemporaryFile(QDir::tempPath() + "/debugwrapper_XXXXXX.js");
+ m_tempWrapper->open();
+ m_tempWrapper->write(jsWrapper.toUtf8());
+ m_tempWrapper->close();
+
+ QFile f(":/debug_harness.html");
+ if (!f.open(QIODevice::ReadOnly))
+ return false;
+ QString html = QString::fromUtf8(f.readAll());
+
+ html = html.arg(m_tempWrapper->fileName());
+ m_tempHarness = new QTemporaryFile(QDir::tempPath() + "/debugharness_XXXXXX.html");
+ m_tempHarness->open();
+ m_tempHarness->write(html.toLocal8Bit());
+ m_tempHarness->close();
+ targetFrame->load(QUrl::fromLocalFile(m_tempHarness->fileName()));
+ return true;
+}
+
+QString Utils::findScript(const QString& jsFilePath, const QString &libraryPath)
+{
+ QString filePath = jsFilePath;
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( libraryPath + '/' + QDir::fromNativeSeparators(jsFilePath) );
+ jsFile.setFileName(libraryPath + '/' + QDir::fromNativeSeparators(jsFilePath));
}
- if ( jsFile.open(QFile::ReadOnly) ) {
- QString scriptBody = jsFileEnc.decode(jsFile.readAll());
- // Remove CLI script heading
- if (scriptBody.startsWith("#!") && !jsFile.fileName().endsWith(COFFEE_SCRIPT_EXTENSION)) {
- scriptBody.prepend("//");
- }
+ return jsFile.fileName();
+ }
+ return QString();
+}
- if (jsFile.fileName().endsWith(COFFEE_SCRIPT_EXTENSION)) {
- QVariant result = Utils::coffee2js(scriptBody);
- if (result.toStringList().at(0) == "false") {
- if (startingScript) {
- Terminal::instance()->cerr(result.toStringList().at(1));
- exit(1);
- } else {
- qWarning() << qPrintable(result.toStringList().at(1));
- scriptBody = QString();
- }
- } else {
- scriptBody = result.toStringList().at(1);
- }
- }
+QString Utils::jsFromScriptFile(const QString& scriptPath, const Encoding& enc)
+{
+ QFile jsFile(scriptPath);
+ if (jsFile.exists() && jsFile.open(QFile::ReadOnly)) {
+ QString scriptBody = enc.decode(jsFile.readAll());
+ // Remove CLI script heading
+ if (scriptBody.startsWith("#!") && !jsFile.fileName().endsWith(COFFEE_SCRIPT_EXTENSION)) {
+ scriptBody.prepend("//");
+ }
- // Execute JS code in the context of the document
- targetFrame->evaluateJavaScript(scriptBody);
- jsFile.close();
- return true;
- } else {
- if (startingScript) {
- Terminal::instance()->cerr(QString("Can't open '%1'").arg(jsFilePath));
+ if (jsFile.fileName().endsWith(COFFEE_SCRIPT_EXTENSION)) {
+ QVariant result = Utils::coffee2js(scriptBody);
+ if (result.toStringList().at(0) == "false") {
+ return QString();
} else {
- qWarning("Can't open '%s'", qPrintable(jsFilePath));
+ scriptBody = result.toStringList().at(1);
}
}
+ jsFile.close();
+
+ return scriptBody;
+ } else {
+ return QString();
}
- return false;
}
+
+void
+Utils::cleanupFromDebug()
+{
+ if (m_tempHarness) {
+ // Will erase the temp file on disk
+ delete m_tempHarness;
+ m_tempHarness = 0;
+ }
+ if (m_tempWrapper) {
+ delete m_tempWrapper;
+ m_tempWrapper = 0;
+ }
+}
+
+
QString Utils::readResourceFileUtf8(const QString &resourceFilePath)
{
QFile f(resourceFilePath);
Oops, something went wrong.

0 comments on commit 61f4434

Please sign in to comment.