diff --git a/src/bootstrap.js b/src/bootstrap.js index 39c98c0d..d4f23a68 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -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; diff --git a/src/config.cpp b/src/config.cpp index e0161ee9..34b68cb9 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -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 diff --git a/src/csconverter.cpp b/src/csconverter.cpp index 4baecb42..646073fa 100644 --- a/src/csconverter.cpp +++ b/src/csconverter.cpp @@ -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; } diff --git a/src/modules/webpage.js b/src/modules/webpage.js index 07c3786b..9080fb43 100644 --- a/src/modules/webpage.js +++ b/src/modules/webpage.js @@ -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); diff --git a/src/phantom.cpp b/src/phantom.cpp index a869971f..43dc182c 100644 --- a/src/phantom.cpp +++ b/src/phantom.cpp @@ -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: diff --git a/src/phantom.h b/src/phantom.h index e01f109e..68fb2891 100644 --- a/src/phantom.h +++ b/src/phantom.h @@ -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(); diff --git a/src/repl.cpp b/src/repl.cpp index ec5487dd..e2321698 100644 --- a/src/repl.cpp +++ b/src/repl.cpp @@ -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); diff --git a/src/utils.cpp b/src/utils.cpp index 56414c5f..c050e89a 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -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; diff --git a/src/webpage.cpp b/src/webpage.cpp index e2b01564..d8a334d0 100644 --- a/src/webpage.cpp +++ b/src/webpage.cpp @@ -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 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) diff --git a/src/webpage.h b/src/webpage.h index b3b1c0c0..5ce43bbb 100644 --- a/src/webpage.h +++ b/src/webpage.h @@ -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();