Merge pull request #82 from Roejames12/master

Exit program properly when we call phantom.exit
1.2
Ariya Hidayat 2011-06-19 23:19:44 -07:00
commit 7ba0d6964e
14 changed files with 237 additions and 47 deletions

View File

@ -74,3 +74,47 @@ window.WebPage = function() {
return page;
}
// override settimeout/setinterval with a try..catch
window._setTimeout = window.setTimeout;
window._setInterval = window.setInterval;
window.setTimeout = function(func, delay) {
var f = function() {
try {
typeof func === 'function' ? func() : eval(func);
} catch (err) {
if (err !== 'phantom.exit') {
throw err;
}
}
}
return window._setTimeout(f, delay);
}
window.setInterval = function(func, delay, lang) {
var f = function() {
try {
typeof func === 'function' ? func() : eval(func);
} catch (err) {
if (err !== 'phantom.exit') {
throw err;
}
}
}
return window._setInterval(f, delay, lang);
}
phantom.exit = function(code) {
if (code == null) {
code = 0;
}
phantom._exit(code);
// halt javascript execution
throw "phantom.exit";
}

View File

@ -19,7 +19,7 @@
import sys
from PyQt4.QtCore import QObject, QFile, qWarning
from PyQt4.QtCore import QObject, QFile
from PyQt4.QtWebKit import QWebPage
@ -43,8 +43,6 @@ class CSConverter(QObject):
[true, this.CoffeeScript.compile(converter.source)];
} catch (error) {
[false, error.message];
}''')
if result[0] is False:
qWarning(result[1])
return ''
return result[1]
}
''')
return result

View File

@ -82,11 +82,13 @@ class Phantom(QObject):
do_action('PhantomInitPost', Bunch(locals()))
def execute(self):
injectJsInFrame(self.m_scriptFile, os.path.dirname(os.path.abspath(__file__)), self.m_page.mainFrame())
injectJsInFrame(self.m_scriptFile, os.path.dirname(os.path.abspath(__file__)), self.m_page.mainFrame(), True)
return not self.m_terminated
def printConsoleMessage(self, msg):
print msg
def printConsoleMessage(self, message, lineNumber, source):
if source:
message = '%s:%d %s' % (source, lineNumber, message)
print message
def returnValue(self):
return self.m_returnValue
@ -95,6 +97,12 @@ class Phantom(QObject):
# Properties and methods exposed to JavaScript
##
@pyqtSlot(int)
def _exit(self, code):
self.m_terminated = True
self.m_returnValue = code
QApplication.instance().exit(code)
@pyqtProperty('QStringList')
def args(self):
return self.m_args
@ -111,13 +119,6 @@ class Phantom(QObject):
def defaultPageSettings(self):
return self.m_defaultPageSettings
@pyqtSlot()
@pyqtSlot(int)
def exit(self, code=0):
self.m_terminated = True
self.m_returnValue = code
QApplication.instance().exit(code)
@pyqtSlot(str, result=bool)
def injectJs(self, filePath):
return injectJsInFrame(filePath, self.libraryPath, self.m_page.mainFrame())

View File

@ -95,7 +95,7 @@ def main():
app.setOrganizationDomain('www.umaclan.com')
app.setApplicationVersion(version)
phantom = Phantom(args, app)
phantom = Phantom(args)
do_action('Main', Bunch(locals()))

View File

@ -2,7 +2,7 @@
# Resource object code
#
# Created: Fri Jun 17 01:01:41 2011
# Created: Sun Jun 19 21:18:36 2011
# by: The Resource Compiler for PyQt (Qt v4.7.2)
#
# WARNING! All changes made in this file will be lost!
@ -10,7 +10,7 @@
from PyQt4 import QtCore
qt_resource_data = "\
\x00\x00\x0a\x29\
\x00\x00\x0d\xdb\
\x2f\
\x2f\x20\x54\x68\x69\x73\x20\x61\x6c\x6c\x6f\x77\x73\x20\x63\x72\
\x65\x61\x74\x69\x6e\x67\x20\x61\x20\x6e\x65\x77\x20\x77\x65\x62\
@ -174,7 +174,66 @@ qt_resource_data = "\
\x65\x6e\x64\x53\x63\x72\x69\x70\x74\x45\x6c\x65\x6d\x65\x6e\x74\
\x28\x73\x63\x72\x69\x70\x74\x55\x72\x6c\x29\x3b\x0a\x20\x20\x20\
\x20\x7d\x3b\x0a\x0a\x20\x20\x20\x20\x72\x65\x74\x75\x72\x6e\x20\
\x70\x61\x67\x65\x3b\x0a\x7d\x0a\
\x70\x61\x67\x65\x3b\x0a\x7d\x0a\x0a\x0a\x2f\x2f\x20\x6f\x76\x65\
\x72\x72\x69\x64\x65\x20\x73\x65\x74\x74\x69\x6d\x65\x6f\x75\x74\
\x2f\x73\x65\x74\x69\x6e\x74\x65\x72\x76\x61\x6c\x20\x77\x69\x74\
\x68\x20\x61\x20\x74\x72\x79\x2e\x2e\x63\x61\x74\x63\x68\x0a\x77\
\x69\x6e\x64\x6f\x77\x2e\x5f\x73\x65\x74\x54\x69\x6d\x65\x6f\x75\
\x74\x20\x3d\x20\x77\x69\x6e\x64\x6f\x77\x2e\x73\x65\x74\x54\x69\
\x6d\x65\x6f\x75\x74\x3b\x0a\x77\x69\x6e\x64\x6f\x77\x2e\x5f\x73\
\x65\x74\x49\x6e\x74\x65\x72\x76\x61\x6c\x20\x3d\x20\x77\x69\x6e\
\x64\x6f\x77\x2e\x73\x65\x74\x49\x6e\x74\x65\x72\x76\x61\x6c\x3b\
\x0a\x77\x69\x6e\x64\x6f\x77\x2e\x73\x65\x74\x54\x69\x6d\x65\x6f\
\x75\x74\x20\x3d\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x66\x75\
\x6e\x63\x2c\x20\x64\x65\x6c\x61\x79\x29\x20\x7b\x0a\x20\x20\x20\
\x20\x76\x61\x72\x20\x66\x20\x3d\x20\x66\x75\x6e\x63\x74\x69\x6f\
\x6e\x28\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x74\x72\
\x79\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
\x74\x79\x70\x65\x6f\x66\x20\x66\x75\x6e\x63\x20\x3d\x3d\x3d\x20\
\x27\x66\x75\x6e\x63\x74\x69\x6f\x6e\x27\x20\x3f\x20\x66\x75\x6e\
\x63\x28\x29\x20\x3a\x20\x65\x76\x61\x6c\x28\x66\x75\x6e\x63\x29\
\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x63\x61\x74\x63\
\x68\x20\x28\x65\x72\x72\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\
\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\x20\x21\x3d\
\x3d\x20\x27\x70\x68\x61\x6e\x74\x6f\x6d\x2e\x65\x78\x69\x74\x27\
\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
\x20\x20\x20\x20\x74\x68\x72\x6f\x77\x20\x65\x72\x72\x3b\x0a\x20\
\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\
\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\
\x20\x20\x72\x65\x74\x75\x72\x6e\x20\x77\x69\x6e\x64\x6f\x77\x2e\
\x5f\x73\x65\x74\x54\x69\x6d\x65\x6f\x75\x74\x28\x66\x2c\x20\x64\
\x65\x6c\x61\x79\x29\x3b\x0a\x7d\x0a\x0a\x77\x69\x6e\x64\x6f\x77\
\x2e\x73\x65\x74\x49\x6e\x74\x65\x72\x76\x61\x6c\x20\x3d\x20\x66\
\x75\x6e\x63\x74\x69\x6f\x6e\x28\x66\x75\x6e\x63\x2c\x20\x64\x65\
\x6c\x61\x79\x2c\x20\x6c\x61\x6e\x67\x29\x20\x7b\x0a\x20\x20\x20\
\x20\x76\x61\x72\x20\x66\x20\x3d\x20\x66\x75\x6e\x63\x74\x69\x6f\
\x6e\x28\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x74\x72\
\x79\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
\x74\x79\x70\x65\x6f\x66\x20\x66\x75\x6e\x63\x20\x3d\x3d\x3d\x20\
\x27\x66\x75\x6e\x63\x74\x69\x6f\x6e\x27\x20\x3f\x20\x66\x75\x6e\
\x63\x28\x29\x20\x3a\x20\x65\x76\x61\x6c\x28\x66\x75\x6e\x63\x29\
\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x63\x61\x74\x63\
\x68\x20\x28\x65\x72\x72\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\
\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\x20\x21\x3d\
\x3d\x20\x27\x70\x68\x61\x6e\x74\x6f\x6d\x2e\x65\x78\x69\x74\x27\
\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
\x20\x20\x20\x20\x74\x68\x72\x6f\x77\x20\x65\x72\x72\x3b\x0a\x20\
\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\
\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\
\x20\x20\x72\x65\x74\x75\x72\x6e\x20\x77\x69\x6e\x64\x6f\x77\x2e\
\x5f\x73\x65\x74\x49\x6e\x74\x65\x72\x76\x61\x6c\x28\x66\x2c\x20\
\x64\x65\x6c\x61\x79\x2c\x20\x6c\x61\x6e\x67\x29\x3b\x0a\x7d\x0a\
\x0a\x0a\x70\x68\x61\x6e\x74\x6f\x6d\x2e\x65\x78\x69\x74\x20\x3d\
\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x63\x6f\x64\x65\x29\x20\
\x7b\x0a\x20\x20\x20\x20\x69\x66\x20\x28\x63\x6f\x64\x65\x20\x3d\
\x3d\x20\x6e\x75\x6c\x6c\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\
\x20\x20\x63\x6f\x64\x65\x20\x3d\x20\x30\x3b\x0a\x20\x20\x20\x20\
\x7d\x0a\x0a\x20\x20\x20\x20\x70\x68\x61\x6e\x74\x6f\x6d\x2e\x5f\
\x65\x78\x69\x74\x28\x63\x6f\x64\x65\x29\x3b\x0a\x0a\x20\x20\x20\
\x20\x2f\x2f\x20\x68\x61\x6c\x74\x20\x6a\x61\x76\x61\x73\x63\x72\
\x69\x70\x74\x20\x65\x78\x65\x63\x75\x74\x69\x6f\x6e\x0a\x20\x20\
\x20\x20\x74\x68\x72\x6f\x77\x20\x22\x70\x68\x61\x6e\x74\x6f\x6d\
\x2e\x65\x78\x69\x74\x22\x3b\x0a\x7d\x0a\
\x00\x00\x56\x27\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@ -4317,8 +4376,8 @@ qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x02\x00\x00\x00\x03\
\x00\x00\x00\x64\x00\x01\x00\x00\x00\x01\x00\x00\x60\x58\
\x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x2d\
\x00\x00\x00\x64\x00\x01\x00\x00\x00\x01\x00\x00\x64\x0a\
\x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xdf\
"
def qInitResources():

View File

@ -106,7 +106,7 @@ def coffee2js(script):
return coffeeScriptConverter.convert(script)
def injectJsInFrame(filePath, libraryPath, targetFrame):
def injectJsInFrame(filePath, libraryPath, targetFrame, startingScript=False):
try:
# if file doesn't exist in the CWD, use the lookup
if not os.path.exists(filePath):
@ -118,7 +118,28 @@ def injectJsInFrame(filePath, libraryPath, targetFrame):
if script.startswith('#!') and not filePath.lower().endswith('.coffee'):
script = '//' + script
targetFrame.evaluateJavaScript(script if not filePath.lower().endswith('.coffee') else coffee2js(script))
if filePath.lower().endswith('.coffee'):
result = coffee2js(script)
if result[0] is False:
if startingScript:
sys.exit(result[1])
else:
qWarning(result[1])
script = ''
else:
script = result[1]
# prepare start script for exiting
if startingScript:
script = '''try { %s } catch (err) {
if (err !== 'phantom.exit') {
phantom._exit(1);
throw err;
}
}
''' % script
targetFrame.evaluateJavaScript(script)
return True
except IOError:
qWarning('No such file or directory: \'%s\'' % filePath)
@ -149,7 +170,7 @@ class SafeStreamFilter(object):
self.target = target
self.encoding = 'utf-8'
self.errors = 'replace'
self.encode_to = self.target.encoding
self.encode_to = self.target.encoding or 'utf-8'
def write(self, s):
s = self.encode(s)

View File

@ -58,9 +58,7 @@ class CustomPage(QWebPage):
self.parent.javaScriptAlertSent.emit(msg)
def javaScriptConsoleMessage(self, message, lineNumber, sourceID):
if sourceID:
message = '%s:%d %s' % (sourceID, lineNumber, message)
self.parent.javaScriptConsoleMessageSent.emit(message)
self.parent.javaScriptConsoleMessageSent.emit(message, lineNumber, sourceID)
def userAgentForUrl(self, url):
return self.m_userAgent
@ -70,7 +68,7 @@ class CustomPage(QWebPage):
class WebPage(QObject):
javaScriptAlertSent = pyqtSignal(str)
javaScriptConsoleMessageSent = pyqtSignal(str)
javaScriptConsoleMessageSent = pyqtSignal(str, int, str)
loadStarted = pyqtSignal()
loadFinished = pyqtSignal(str)
resourceReceived = pyqtSignal('QVariantMap')

View File

@ -74,3 +74,47 @@ window.WebPage = function() {
return page;
}
// override settimeout/setinterval with a try..catch
window._setTimeout = window.setTimeout;
window._setInterval = window.setInterval;
window.setTimeout = function(func, delay) {
var f = function() {
try {
typeof func === 'function' ? func() : eval(func);
} catch (err) {
if (err !== 'phantom.exit') {
throw err;
}
}
}
return window._setTimeout(f, delay);
}
window.setInterval = function(func, delay, lang) {
var f = function() {
try {
typeof func === 'function' ? func() : eval(func);
} catch (err) {
if (err !== 'phantom.exit') {
throw err;
}
}
}
return window._setInterval(f, delay, lang);
}
phantom.exit = function(code) {
if (code == null) {
code = 0;
}
phantom._exit(code);
// halt javascript execution
throw "phantom.exit";
}

View File

@ -49,7 +49,7 @@ CSConverter::CSConverter(QObject *parent)
m_webPage.mainFrame()->addToJavaScriptWindowObject("converter", this);
}
QString CSConverter::convert(const QString &script)
QVariant CSConverter::convert(const QString &script)
{
setProperty("source", script);
QVariant result = m_webPage.mainFrame()->evaluateJavaScript("try {" \
@ -57,9 +57,5 @@ QString CSConverter::convert(const QString &script)
"} catch (error) {" \
" [false, error.message];" \
"}");
if (result.toStringList().at(0) == "false") {
qWarning(qPrintable(result.toStringList().at(1)));
return QString();
}
return result.toStringList().at(1);
return result;
}

View File

@ -37,7 +37,7 @@ class CSConverter: public QObject
{
public:
CSConverter(QObject *parent = 0);
QString convert(const QString &script);
QVariant convert(const QString &script);
private:
QWebPage m_webPage;

View File

@ -195,7 +195,7 @@ bool Phantom::execute()
if (m_scriptFile.isEmpty())
return false;
if (!Utils::injectJsInFrame(m_scriptFile, QDir::currentPath(), m_page->mainFrame())) {
if (!Utils::injectJsInFrame(m_scriptFile, QDir::currentPath(), m_page->mainFrame(), true)) {
m_returnValue = -1;
return false;
}
@ -226,7 +226,7 @@ QObject *Phantom::createWebPage()
return page;
}
void Phantom::exit(int code)
void Phantom::_exit(int code)
{
m_terminated = true;
m_returnValue = code;

View File

@ -66,7 +66,7 @@ public:
public slots:
QObject *createWebPage();
bool injectJs(const QString &jsFilePath);
void exit(int code = 0);
void _exit(int code);
private slots:
void printConsoleMessage(const QString &msg, int lineNumber, const QString &source);

View File

@ -69,7 +69,7 @@ void Utils::messageHandler(QtMsgType type, const char *msg)
}
}
QString Utils::coffee2js(const QString &script)
QVariant 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;
@ -80,7 +80,7 @@ QString Utils::coffee2js(const QString &script)
return coffeeScriptConverter->convert(script);
}
bool Utils::injectJsInFrame(const QString &jsFilePath, const QString &libraryPath, QWebFrame *targetFrame)
bool Utils::injectJsInFrame(const QString &jsFilePath, const QString &libraryPath, QWebFrame *targetFrame, const bool startingScript)
{
// Don't do anything if an empty string is passed
if (!jsFilePath.isEmpty()) {
@ -100,14 +100,43 @@ bool Utils::injectJsInFrame(const QString &jsFilePath, const QString &libraryPat
scriptBody.prepend("//");
}
if (jsFile.fileName().endsWith(COFFEE_SCRIPT_EXTENSION)) {
QVariant result = Utils::coffee2js(scriptBody);
if (result.toStringList().at(0) == "false") {
if (startingScript) {
std::cerr << qPrintable(result.toStringList().at(1)) << std::endl;
exit(1);
} else {
qWarning() << qPrintable(result.toStringList().at(1));
scriptBody = QString();
}
} else {
scriptBody = result.toStringList().at(1);
}
}
// prepare start script for exiting
if (startingScript) {
scriptBody = QString("try {" \
" %1" \
"} catch (err) {" \
" if (err !== 'phantom.exit') {" \
" phantom._exit(1);" \
" throw err;" \
" }" \
"}").arg(scriptBody);
}
// 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);
targetFrame->evaluateJavaScript(scriptBody);
jsFile.close();
return true;
} else {
qWarning() << "Can't open '" << qPrintable(jsFilePath) << "'";
if (startingScript) {
std::cerr << "Can't open '" << qPrintable(jsFilePath) << "'" << std::endl;
} else {
qWarning("Can't open '%s'", qPrintable(jsFilePath));
}
}
}
return false;

View File

@ -45,8 +45,8 @@ class Utils
public:
static void showUsage();
static void messageHandler(QtMsgType type, const char *msg);
static QString coffee2js(const QString &script);
static bool injectJsInFrame(const QString &jsFilePath, const QString &libraryPath, QWebFrame *targetFrame);
static QVariant coffee2js(const QString &script);
static bool injectJsInFrame(const QString &jsFilePath, const QString &libraryPath, QWebFrame *targetFrame, const bool startingScript = false);
private:
Utils(); //< This class shouldn't be instantiated