Fix memory leak of response.

Turns Response into a QObject and sets parent to the
command that emits it.

Each Command is also a child of the decorator commands,
Timeout and PageLoading commnds, so that deleting the
top level command will delete all the children.

See discussion in #430.
This commit is contained in:
Sean Geoghegan 2012-12-12 17:17:54 +10:30 committed by Matthew Horan
parent 1cc1428ee4
commit 77811ca9ba
44 changed files with 83 additions and 59 deletions

View File

@ -13,6 +13,6 @@ void Authenticate::start() {
networkAccessManager->setUserName(username); networkAccessManager->setUserName(username);
networkAccessManager->setPassword(password); networkAccessManager->setPassword(password);
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -9,5 +9,5 @@ ClearCookies::ClearCookies(WebPageManager *manager, QStringList &arguments, QObj
void ClearCookies::start() void ClearCookies::start()
{ {
manager()->cookieJar()->clearCookies(); manager()->cookieJar()->clearCookies();
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -7,5 +7,5 @@ ClearPromptText::ClearPromptText(WebPageManager *manager, QStringList &arguments
void ClearPromptText::start() void ClearPromptText::start()
{ {
page()->setPromptText(QString()); page()->setPromptText(QString());
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -6,3 +6,12 @@ Command::Command(QObject *parent) : QObject(parent) {
QString Command::toString() const { QString Command::toString() const {
return metaObject()->className(); return metaObject()->className();
} }
void Command::emitFinished(bool success) {
emit finished(new Response(success, this));
}
void Command::emitFinished(bool success, QString message) {
emit finished(new Response(success, message, this));
}

View File

@ -3,6 +3,7 @@
#include <QObject> #include <QObject>
#include "Response.h" #include "Response.h"
#include <QString>
class Command : public QObject { class Command : public QObject {
Q_OBJECT Q_OBJECT
@ -12,6 +13,10 @@ class Command : public QObject {
virtual void start() = 0; virtual void start() = 0;
virtual QString toString() const; virtual QString toString() const;
protected:
void emitFinished(bool success);
void emitFinished(bool success, QString message);
signals: signals:
void finished(Response *response); void finished(Response *response);
}; };

View File

@ -43,13 +43,15 @@ void Connection::pendingLoadFinished(bool success) {
void Connection::writePageLoadFailure() { void Connection::writePageLoadFailure() {
m_pageSuccess = true; m_pageSuccess = true;
QString message = currentPage()->failureString(); QString message = currentPage()->failureString();
writeResponse(new Response(false, message)); Response *response = new Response(false, message, this);
writeResponse(response);
delete response;
} }
void Connection::finishCommand(Response *response) { void Connection::finishCommand(Response *response) {
m_pageSuccess = true; m_pageSuccess = true;
sender()->deleteLater();
writeResponse(response); writeResponse(response);
sender()->deleteLater();
} }
void Connection::writeResponse(Response *response) { void Connection::writeResponse(Response *response) {
@ -64,7 +66,6 @@ void Connection::writeResponse(Response *response) {
QString messageLength = QString::number(messageUtf8.size()) + "\n"; QString messageLength = QString::number(messageUtf8.size()) + "\n";
m_socket->write(messageLength.toAscii()); m_socket->write(messageLength.toAscii());
m_socket->write(messageUtf8); m_socket->write(messageUtf8);
delete response;
} }
WebPage *Connection::currentPage() { WebPage *Connection::currentPage() {

View File

@ -6,6 +6,6 @@ ConsoleMessages::ConsoleMessages(WebPageManager *manager, QStringList &arguments
} }
void ConsoleMessages::start() { void ConsoleMessages::start() {
emit finished(new Response(true, page()->consoleMessages())); emitFinished(true, page()->consoleMessages());
} }

View File

@ -9,6 +9,6 @@ void CurrentUrl::start() {
QStringList arguments; QStringList arguments;
QVariant result = page()->invokeCapybaraFunction("currentUrl", arguments); QVariant result = page()->invokeCapybaraFunction("currentUrl", arguments);
QString url = result.toString(); QString url = result.toString();
emit finished(new Response(true, url)); emitFinished(true, url);
} }

View File

@ -6,5 +6,5 @@ EnableLogging::EnableLogging(WebPageManager *manager, QStringList &arguments, QO
void EnableLogging::start() { void EnableLogging::start() {
manager()->enableLogging(); manager()->enableLogging();
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -10,7 +10,7 @@ Evaluate::Evaluate(WebPageManager *manager, QStringList &arguments, QObject *par
void Evaluate::start() { void Evaluate::start() {
QVariant result = page()->currentFrame()->evaluateJavaScript(arguments()[0]); QVariant result = page()->currentFrame()->evaluateJavaScript(arguments()[0]);
addVariant(result); addVariant(result);
emit finished(new Response(true, m_buffer)); emitFinished(true, m_buffer);
} }
void Evaluate::addVariant(QVariant &object) { void Evaluate::addVariant(QVariant &object) {

View File

@ -9,9 +9,9 @@ void Execute::start() {
QString script = arguments()[0] + QString("; 'success'"); QString script = arguments()[0] + QString("; 'success'");
QVariant result = page()->currentFrame()->evaluateJavaScript(script); QVariant result = page()->currentFrame()->evaluateJavaScript(script);
if (result.isValid()) { if (result.isValid()) {
emit finished(new Response(true)); emitFinished(true);
} else { } else {
emit finished(new Response(false, QString("Javascript failed to execute"))); emitFinished(false, QString("Javascript failed to execute"));
} }
} }

View File

@ -12,9 +12,9 @@ void Find::start() {
if (result.isValid()) { if (result.isValid()) {
message = result.toString(); message = result.toString();
emit finished(new Response(true, message)); emitFinished(true, message);
} else { } else {
emit finished(new Response(false, QString("Invalid XPath expression"))); emitFinished(false, QString("Invalid XPath expression"));
} }
} }

View File

@ -51,7 +51,7 @@ void FrameFocus::focusId(QString name) {
void FrameFocus::focusParent() { void FrameFocus::focusParent() {
if (page()->currentFrame()->parentFrame() == 0) { if (page()->currentFrame()->parentFrame() == 0) {
emit finished(new Response(false, QString("Already at parent frame."))); emitFinished(false, QString("Already at parent frame."));
} else { } else {
page()->currentFrame()->parentFrame()->setFocus(); page()->currentFrame()->parentFrame()->setFocus();
success(); success();
@ -59,9 +59,9 @@ void FrameFocus::focusParent() {
} }
void FrameFocus::frameNotFound() { void FrameFocus::frameNotFound() {
emit finished(new Response(false, QString("Unable to locate frame. "))); emitFinished(false, QString("Unable to locate frame. "));
} }
void FrameFocus::success() { void FrameFocus::success() {
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -15,5 +15,5 @@ void GetCookies::start()
m_buffer.append(cookie.toRawForm()); m_buffer.append(cookie.toRawForm());
m_buffer.append("\n"); m_buffer.append("\n");
} }
emit finished(new Response(true, m_buffer)); emitFinished(true, m_buffer);
} }

View File

@ -5,5 +5,5 @@ GetTimeout::GetTimeout(WebPageManager *manager, QStringList &arguments, QObject
} }
void GetTimeout::start() { void GetTimeout::start() {
emit finished(new Response(true, QString::number(manager()->getTimeout()))); emitFinished(true, QString::number(manager()->getTimeout()));
} }

View File

@ -7,5 +7,5 @@ GetWindowHandle::GetWindowHandle(WebPageManager *manager, QStringList &arguments
} }
void GetWindowHandle::start() { void GetWindowHandle::start() {
emit finished(new Response(true, page()->uuid())); emitFinished(true, page()->uuid());
} }

View File

@ -16,5 +16,5 @@ void GetWindowHandles::start() {
handles += stringList.join(",") + "]"; handles += stringList.join(",") + "]";
emit finished(new Response(true, handles)); emitFinished(true, handles);
} }

View File

@ -15,5 +15,5 @@ void Header::start() {
} else { } else {
networkAccessManager->addHeader(key, value); networkAccessManager->addHeader(key, value);
} }
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -11,6 +11,6 @@ void Headers::start() {
foreach(QNetworkReply::RawHeaderPair header, page()->pageHeaders()) foreach(QNetworkReply::RawHeaderPair header, page()->pageHeaders())
headers << header.first+": "+header.second; headers << header.first+": "+header.second;
emit finished(new Response(true, headers.join("\n"))); emitFinished(true, headers.join("\n"));
} }

View File

@ -8,6 +8,6 @@ IgnoreSslErrors::IgnoreSslErrors(WebPageManager *manager, QStringList &arguments
void IgnoreSslErrors::start() { void IgnoreSslErrors::start() {
manager()->setIgnoreSslErrors(true); manager()->setIgnoreSslErrors(true);
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -6,5 +6,5 @@ JavascriptAlertMessages::JavascriptAlertMessages(WebPageManager *manager, QStrin
void JavascriptAlertMessages::start() void JavascriptAlertMessages::start()
{ {
emit finished(new Response(true, page()->alertMessages())); emitFinished(true, page()->alertMessages());
} }

View File

@ -6,5 +6,5 @@ JavascriptConfirmMessages::JavascriptConfirmMessages(WebPageManager *manager, QS
void JavascriptConfirmMessages::start() void JavascriptConfirmMessages::start()
{ {
emit finished(new Response(true, page()->confirmMessages())); emitFinished(true, page()->confirmMessages());
} }

View File

@ -6,5 +6,5 @@ JavascriptPromptMessages::JavascriptPromptMessages(WebPageManager *manager, QStr
void JavascriptPromptMessages::start() void JavascriptPromptMessages::start()
{ {
emit finished(new Response(true, page()->promptMessages())); emitFinished(true, page()->promptMessages());
} }

View File

@ -10,7 +10,7 @@ void Node::start() {
QString functionName = functionArguments.takeFirst(); QString functionName = functionArguments.takeFirst();
QVariant result = page()->invokeCapybaraFunction(functionName, functionArguments); QVariant result = page()->invokeCapybaraFunction(functionName, functionArguments);
QString attributeValue = result.toString(); QString attributeValue = result.toString();
emit finished(new Response(true, attributeValue)); emitFinished(true, attributeValue);
} }
QString Node::toString() const { QString Node::toString() const {

View File

@ -8,6 +8,6 @@ NullCommand::NullCommand(QString name, QObject *parent) : Command(parent) {
void NullCommand::start() { void NullCommand::start() {
QString failure = QString("[Capybara WebKit] Unknown command: ") + m_name + "\n"; QString failure = QString("[Capybara WebKit] Unknown command: ") + m_name + "\n";
emit finished(new Response(false, failure)); emitFinished(false, failure);
} }

View File

@ -9,6 +9,7 @@ PageLoadingCommand::PageLoadingCommand(Command *command, WebPageManager *manager
m_pageLoadingFromCommand = false; m_pageLoadingFromCommand = false;
m_pageSuccess = true; m_pageSuccess = true;
m_pendingResponse = NULL; m_pendingResponse = NULL;
m_command->setParent(this);
} }
void PageLoadingCommand::start() { void PageLoadingCommand::start() {
@ -29,7 +30,7 @@ void PageLoadingCommand::pendingLoadFinished(bool success) {
emit finished(m_pendingResponse); emit finished(m_pendingResponse);
} else { } else {
QString message = m_manager->currentPage()->failureString(); QString message = m_manager->currentPage()->failureString();
emit finished(new Response(false, message)); emitFinished(false, message);
} }
} }
} }
@ -43,7 +44,7 @@ void PageLoadingCommand::pageLoadingFromCommand() {
void PageLoadingCommand::commandFinished(Response *response) { void PageLoadingCommand::commandFinished(Response *response) {
disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand())); disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
m_manager->logger() << "Finished" << m_command->toString() << "with response" << response->toString(); m_manager->logger() << "Finished" << m_command->toString() << "with response" << response->toString();
m_command->deleteLater();
if (m_pageLoadingFromCommand) if (m_pageLoadingFromCommand)
m_pendingResponse = response; m_pendingResponse = response;
else else

View File

@ -15,5 +15,5 @@ void Render::start() {
bool result = page()->render( imagePath ); bool result = page()->render( imagePath );
emit finished(new Response(result)); emitFinished(result);
} }

View File

@ -10,6 +10,6 @@ void Reset::start() {
manager()->reset(); manager()->reset();
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -12,6 +12,6 @@ void ResizeWindow::start() {
QSize size(width, height); QSize size(width, height);
page()->setViewportSize(size); page()->setViewportSize(size);
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -1,17 +1,17 @@
#include "Response.h" #include "Response.h"
#include <iostream> #include <iostream>
Response::Response(bool success, QString message) { Response::Response(bool success, QString message, QObject *parent) : QObject(parent) {
m_success = success; m_success = success;
m_message = message.toUtf8(); m_message = message.toUtf8();
} }
Response::Response(bool success, QByteArray message) { Response::Response(bool success, QByteArray message, QObject *parent) : QObject(parent) {
m_success = success; m_success = success;
m_message = message; m_message = message;
} }
Response::Response(bool success) { Response::Response(bool success, QObject *parent) : QObject(parent) {
m_success = success; m_success = success;
} }

View File

@ -1,11 +1,17 @@
#ifndef RESPONSE_H
#define RESPONSE_H
#include <QObject>
#include <QString> #include <QString>
#include <QByteArray> #include <QByteArray>
class Response { class Response : public QObject {
Q_OBJECT
public: public:
Response(bool success, QString message); Response(bool success, QString message, QObject *parent);
Response(bool success, QByteArray message); Response(bool success, QByteArray message, QObject *parent);
Response(bool success); Response(bool success, QObject *parent);
bool isSuccess() const; bool isSuccess() const;
QByteArray message() const; QByteArray message() const;
QString toString() const; QString toString() const;
@ -14,3 +20,6 @@ class Response {
bool m_success; bool m_success;
QByteArray m_message; QByteArray m_message;
}; };
#endif

View File

@ -7,5 +7,5 @@ SetConfirmAction::SetConfirmAction(WebPageManager *manager, QStringList &argumen
void SetConfirmAction::start() void SetConfirmAction::start()
{ {
page()->setConfirmAction(arguments()[0]); page()->setConfirmAction(arguments()[0]);
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -11,5 +11,5 @@ void SetCookie::start()
QList<QNetworkCookie> cookies = QNetworkCookie::parseCookies(arguments()[0].toAscii()); QList<QNetworkCookie> cookies = QNetworkCookie::parseCookies(arguments()[0].toAscii());
NetworkCookieJar *jar = manager()->cookieJar(); NetworkCookieJar *jar = manager()->cookieJar();
jar->overwriteCookies(cookies); jar->overwriteCookies(cookies);
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -7,5 +7,5 @@ SetPromptAction::SetPromptAction(WebPageManager *manager, QStringList &arguments
void SetPromptAction::start() void SetPromptAction::start()
{ {
page()->setPromptAction(arguments()[0]); page()->setPromptAction(arguments()[0]);
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -7,5 +7,5 @@ SetPromptText::SetPromptText(WebPageManager *manager, QStringList &arguments, QO
void SetPromptText::start() void SetPromptText::start()
{ {
page()->setPromptText(arguments()[0]); page()->setPromptText(arguments()[0]);
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -19,5 +19,5 @@ void SetProxy::start()
arguments()[3]); arguments()[3]);
page()->networkAccessManager()->setProxy(proxy); page()->networkAccessManager()->setProxy(proxy);
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -8,5 +8,5 @@ SetSkipImageLoading::SetSkipImageLoading(WebPageManager *manager, QStringList &a
void SetSkipImageLoading::start() { void SetSkipImageLoading::start() {
page()->setSkipImageLoading(arguments().contains("true")); page()->setSkipImageLoading(arguments().contains("true"));
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -11,9 +11,9 @@ void SetTimeout::start() {
if (ok) { if (ok) {
manager()->setTimeout(timeout); manager()->setTimeout(timeout);
emit finished(new Response(true)); emitFinished(true);
} else { } else {
emit finished(new Response(false, QString("Invalid value for timeout"))); emitFinished(false, QString("Invalid value for timeout"));
} }
} }

View File

@ -10,6 +10,6 @@ SetUrlBlacklist::SetUrlBlacklist(WebPageManager *manager, QStringList &arguments
void SetUrlBlacklist::start() { void SetUrlBlacklist::start() {
NetworkAccessManager* networkAccessManager = page()->networkAccessManager(); NetworkAccessManager* networkAccessManager = page()->networkAccessManager();
networkAccessManager->setUrlBlacklist(arguments()); networkAccessManager->setUrlBlacklist(arguments());
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -8,6 +8,6 @@ Status::Status(WebPageManager *manager, QStringList &arguments, QObject *parent)
void Status::start() { void Status::start() {
int status = page()->getLastStatus(); int status = page()->getLastStatus();
emit finished(new Response(true, QString::number(status))); emitFinished(true, QString::number(status));
} }

View File

@ -10,6 +10,7 @@ TimeoutCommand::TimeoutCommand(Command *command, WebPageManager *manager, QObjec
m_manager = manager; m_manager = manager;
m_timer = new QTimer(this); m_timer = new QTimer(this);
m_timer->setSingleShot(true); m_timer->setSingleShot(true);
m_command->setParent(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout())); connect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
connect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand())); connect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
} }
@ -44,7 +45,7 @@ void TimeoutCommand::pendingLoadFinished(bool success) {
disconnect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout())); disconnect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand())); disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool))); disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
emit finished(new Response(false, m_manager->currentPage()->failureString())); emitFinished(false, m_manager->currentPage()->failureString());
} }
} }
@ -57,15 +58,13 @@ void TimeoutCommand::commandTimeout() {
disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool))); disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
disconnect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *))); disconnect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *)));
m_manager->currentPage()->triggerAction(QWebPage::Stop); m_manager->currentPage()->triggerAction(QWebPage::Stop);
m_command->deleteLater(); emit finished(new Response(false, QString("timeout"), this));
emit finished(new Response(false, QString("timeout")));
} }
void TimeoutCommand::commandFinished(Response *response) { void TimeoutCommand::commandFinished(Response *response) {
disconnect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout())); disconnect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand())); disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool))); disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
m_command->deleteLater();
emit finished(response); emit finished(response);
} }

View File

@ -9,5 +9,5 @@ Visit::Visit(WebPageManager *manager, QStringList &arguments, QObject *parent) :
void Visit::start() { void Visit::start() {
QUrl requestedUrl = QUrl::fromEncoded(arguments()[0].toUtf8(), QUrl::StrictMode); QUrl requestedUrl = QUrl::fromEncoded(arguments()[0].toUtf8(), QUrl::StrictMode);
page()->currentFrame()->load(QUrl(requestedUrl)); page()->currentFrame()->load(QUrl(requestedUrl));
emit finished(new Response(true)); emitFinished(true);
} }

View File

@ -12,12 +12,12 @@ void WindowFocus::start() {
} }
void WindowFocus::windowNotFound() { void WindowFocus::windowNotFound() {
emit finished(new Response(false, QString("Unable to locate window. "))); emitFinished(false, QString("Unable to locate window. "));
} }
void WindowFocus::success(WebPage *page) { void WindowFocus::success(WebPage *page) {
page->setFocus(); page->setFocus();
emit finished(new Response(true)); emitFinished(true);
} }
void WindowFocus::focusWindow(QString selector) { void WindowFocus::focusWindow(QString selector) {

View File

@ -12,5 +12,5 @@ void Body::start() {
else else
result = page()->currentFrame()->toHtml(); result = page()->currentFrame()->toHtml();
emit finished(new Response(true, result)); emitFinished(true, result);
} }