support for backtraces (part 2).

add a default error handler on all pages. people can override if they
need.
ensure error handler can be removed without errors.

Hack ScriptSourceCode so we can pass in a raw string and not have it
validated as a URL

change source location hint for webpage.evaluate().

http://code.google.com/p/phantomjs/issues/detail?id=166
 Please enter the commit message for your changes. Lines starting
1.5
Jon Leighton 2012-03-17 10:27:13 -07:00 committed by Ariya Hidayat
parent afe570484f
commit a48770cba3
10 changed files with 99 additions and 24 deletions

View File

@ -34,7 +34,6 @@
*/
function require(name) {
var code, func, exports;
if (name === 'webpage' || name === 'fs' || name === 'webserver' || name === 'system') {
@ -55,5 +54,35 @@ function require(name) {
}
}
(function() {
var handler;
var signal = phantom.page.javaScriptErrorSent;
phantom.__defineSetter__('onError', function(f) {
if (handler && typeof handler === 'function') {
try { signal.disconnect(handler) } catch (e) {}
}
handler = f;
if (typeof f === 'function') {
signal.connect(f);
}
})
})();
// TODO: Make this output to STDERR
phantom.defaultErrorHandler = function(error, backtrace) {
console.log(error + "\n");
backtrace.forEach(function(item) {
var message = item.file + ":" + item.line;
if (item.function) message += " in " + item.function
console.log(" " + message);
})
}
phantom.onError = phantom.defaultErrorHandler;
// Legacy way to use WebPage
window.WebPage = require('webpage').create;

View File

@ -205,7 +205,7 @@ void Config::loadJsonFile(const QString &filePath)
// Add this object to the global scope
webPage.mainFrame()->addToJavaScriptWindowObject("config", this);
// Apply the JSON config settings to this very object
webPage.mainFrame()->evaluateJavaScript(configurator.arg(jsonConfig));
webPage.mainFrame()->evaluateJavaScript(configurator.arg(jsonConfig), QString());
}
bool Config::autoLoadImages() const

View File

@ -48,17 +48,23 @@ CSConverter *CSConverter::instance()
CSConverter::CSConverter()
: QObject(QCoreApplication::instance())
{
m_webPage.mainFrame()->evaluateJavaScript(Utils::readResourceFileUtf8(":/coffee-script.js"));
m_webPage.mainFrame()->evaluateJavaScript(
Utils::readResourceFileUtf8(":/coffee-script.js"),
QString("phantomjs://coffee-script.js")
);
m_webPage.mainFrame()->addToJavaScriptWindowObject("converter", this);
}
QVariant CSConverter::convert(const QString &script)
{
setProperty("source", script);
QVariant result = m_webPage.mainFrame()->evaluateJavaScript("try {" \
" [true, this.CoffeeScript.compile(converter.source)];" \
"} catch (error) {" \
" [false, error.message];" \
"}");
QVariant result = m_webPage.mainFrame()->evaluateJavaScript(
"try {" \
" [true, this.CoffeeScript.compile(converter.source)];" \
"} catch (error) {" \
" [false, error.message];" \
"}",
QString()
);
return result;
}

View File

@ -99,8 +99,12 @@ exports.create = function (opts) {
this[signalName].disconnect(handlers[signalName]);
} catch (e) {}
}
handlers[signalName] = f;
this[signalName].connect(handlers[signalName]);
if (typeof f === 'function') {
this[signalName].connect(f);
}
});
}
@ -121,6 +125,10 @@ exports.create = function (opts) {
defineSetter("onConsoleMessage", "javaScriptConsoleMessageSent");
defineSetter("onError", "javaScriptErrorSent");
page.onError = phantom.defaultErrorHandler;
page.open = function (url, arg1, arg2, arg3, arg4) {
if (arguments.length === 1) {
this.openUrl(url, 'get', this.settings);

View File

@ -122,8 +122,6 @@ Phantom::Phantom(QObject *parent)
m_page->applySettings(m_defaultPageSettings);
setLibraryPath(QFileInfo(m_config.scriptFile()).dir().absolutePath());
onInitialized();
}
Phantom::~Phantom()
@ -208,6 +206,11 @@ QVariantMap Phantom::version() const
return result;
}
QObject *Phantom::page() const
{
return m_page;
}
// public slots:
QObject *Phantom::createWebPage()
{
@ -287,10 +290,7 @@ void Phantom::debugExit(int code)
// private slots:
void Phantom::printConsoleMessage(const QString &message, int lineNumber, const QString &source)
{
QString msg = message;
if (!source.isEmpty())
msg = source + ":" + QString::number(lineNumber) + " " + msg;
Terminal::instance()->cout(msg);
Terminal::instance()->cout(message);
}
void Phantom::onInitialized()
@ -299,7 +299,10 @@ void Phantom::onInitialized()
m_page->mainFrame()->addToJavaScriptWindowObject("phantom", this);
// Bootstrap the PhantomJS scope
m_page->mainFrame()->evaluateJavaScript(Utils::readResourceFileUtf8(":/bootstrap.js"));
m_page->mainFrame()->evaluateJavaScript(
Utils::readResourceFileUtf8(":/bootstrap.js"),
QString("phantomjs://bootstrap.js")
);
}
// private:

View File

@ -52,6 +52,7 @@ class Phantom: public REPLCompletable
Q_PROPERTY(QString outputEncoding READ outputEncoding WRITE setOutputEncoding)
Q_PROPERTY(QString scriptName READ scriptName)
Q_PROPERTY(QVariantMap version READ version)
Q_PROPERTY(QObject *page READ page)
public:
Phantom(QObject *parent = 0);
@ -74,6 +75,8 @@ public:
QVariantMap version() const;
QObject* page() const;
public slots:
QObject *createWebPage();
QObject *createWebServer();

View File

@ -99,7 +99,7 @@ REPL::REPL(QWebFrame *webframe, Phantom *parent)
linenoiseSetCompletionCallback(REPL::offerCompletion);
// Inject REPL utility functions
m_webframe->evaluateJavaScript(Utils::readResourceFileUtf8(":/repl.js"));
m_webframe->evaluateJavaScript(Utils::readResourceFileUtf8(":/repl.js"), QString("phantomjs://repl.js"));
// Start the REPL's loop
QTimer::singleShot(0, this, SLOT(startLoop()));
@ -133,7 +133,8 @@ void REPL::offerCompletion(const char *buf, linenoiseCompletions *lc)
QStringList completions = REPL::getInstance()->m_webframe->evaluateJavaScript(
QString(JS_RETURN_POSSIBLE_COMPLETIONS).arg(
toInspect,
toComplete)
toComplete),
QString()
).toStringList();
foreach (QString c, completions) {
@ -158,7 +159,7 @@ void REPL::startLoop()
// Send the user input to the main Phantom frame for evaluation
m_webframe->evaluateJavaScript(
QString(JS_EVAL_USER_INPUT).arg(
QString(userInput).replace('"', "\\\"")));
QString(userInput).replace('"', "\\\"")), QString("phantomjs://repl-input"));
// Save command in the REPL history
linenoiseHistoryAdd(userInput);

View File

@ -94,7 +94,7 @@ bool Utils::injectJsInFrame(const QString &jsFilePath, const Encoding &jsFileEnc
return false;
}
// Execute JS code in the context of the document
targetFrame->evaluateJavaScript(scriptBody);
targetFrame->evaluateJavaScript(scriptBody, jsFilePath);
return true;
}
@ -113,7 +113,7 @@ bool Utils::loadJSForDebug(const QString& jsFilePath, const Encoding& jsFileEnc,
targetFrame->setHtml(remoteDebuggerHarnessSrc);
if (autorun) {
targetFrame->evaluateJavaScript("__run()");
targetFrame->evaluateJavaScript("__run()", QString());
}
return true;

View File

@ -92,6 +92,10 @@ protected:
m_webPage->emitConsoleMessage(message, lineNumber, sourceID);
}
void javaScriptError(const QWebPage::JavaScriptError& error) {
m_webPage->emitError(error);
}
QString userAgentForUrl(const QUrl &url) const {
Q_UNUSED(url);
return m_userAgent;
@ -287,7 +291,7 @@ QVariantMap WebPage::paperSize() const
QVariant WebPage::evaluate(const QString &code)
{
QString function = "(" + code + ")()";
return m_mainFrame->evaluateJavaScript(function);
return m_mainFrame->evaluateJavaScript(function, QString("phantomjs://webpage.evaluate()"));
}
void WebPage::emitAlert(const QString &msg)
@ -300,6 +304,25 @@ void WebPage::emitConsoleMessage(const QString &message, int lineNumber, const Q
emit javaScriptConsoleMessageSent(message, lineNumber, source);
}
void WebPage::emitError(const QWebPage::JavaScriptError& error)
{
QList<QWebPage::JavaScriptFrame> backtrace = error.backtrace();
QVariantList newBacktrace = QVariantList();
for (int i = 0; i < backtrace.size(); ++i) {
QWebPage::JavaScriptFrame frame = backtrace.at(i);
QVariantMap newFrame = QVariantMap();
newFrame["file"] = frame.file();
newFrame["line"] = frame.line();
newFrame["function"] = frame.function();
newBacktrace << newFrame;
}
emit javaScriptErrorSent(error.message(), newBacktrace);
}
void WebPage::finish(bool ok)
{
QString status = ok ? "success" : "fail";
@ -349,7 +372,7 @@ void WebPage::openUrl(const QString &address, const QVariant &op, const QVariant
networkOp = QNetworkAccessManager::DeleteOperation;
if (networkOp == QNetworkAccessManager::UnknownOperation) {
m_mainFrame->evaluateJavaScript("console.error('Unknown network operation: " + operation + "');");
m_mainFrame->evaluateJavaScript("console.error('Unknown network operation: " + operation + "');", QString());
return;
}
@ -598,7 +621,7 @@ bool WebPage::injectJs(const QString &jsFilePath) {
}
void WebPage::_appendScriptElement(const QString &scriptUrl) {
m_mainFrame->evaluateJavaScript( QString(JS_APPEND_SCRIPT_ELEMENT).arg(scriptUrl) );
m_mainFrame->evaluateJavaScript( QString(JS_APPEND_SCRIPT_ELEMENT).arg(scriptUrl), scriptUrl );
}
void WebPage::sendEvent(const QString &type, const QVariant &arg1, const QVariant &arg2)

View File

@ -102,6 +102,7 @@ signals:
void loadFinished(const QString &status);
void javaScriptAlertSent(const QString &msg);
void javaScriptConsoleMessageSent(const QString &message, int lineNumber, const QString &source);
void javaScriptErrorSent(const QString &message, const QVariantList &backtrace);
void resourceRequested(const QVariant &req);
void resourceReceived(const QVariant &resource);
@ -116,6 +117,7 @@ private:
void emitAlert(const QString &msg);
void emitConsoleMessage(const QString &msg, int lineNumber, const QString &source);
void emitError(const QWebPage::JavaScriptError& error);
virtual void initCompletions();