From 182cb2e5b7871ec14ee8dbcaf6dfe4bfd5c89894 Mon Sep 17 00:00:00 2001 From: Aaron Gibralter Date: Tue, 19 Apr 2011 03:16:25 -0400 Subject: [PATCH] Working on frame handling. In terms of the QT code, the non-frame-related specs pass with these changes (e.g. mainFrame() => currentFrame() and the new injectJavascriptHelpers() code in WebPage.cpp). It seems like the current JS+xpath implementation dives in to iframes already. Is this desired behavior? I wonder if that works with x-domain iframes? I doubt it... Also, this design assumes that we only step one frame down at a time... Lastly, I'm really not sure how QWebKit decides which frame is currentFrame(). For now, I'm hoping to be able to use the QWebFrame setFocus() method. This may be a dead end though. We may have to have WebPage manually keep track of the "current" frame. --- lib/capybara/driver/webkit.rb | 4 +- lib/capybara/driver/webkit/browser.rb | 8 +++ spec/driver_spec.rb | 83 +++++++++++++++++++++++++++ src/Connection.cpp | 1 + src/Evaluate.cpp | 2 +- src/Execute.cpp | 2 +- src/FrameFocus.cpp | 12 ++++ src/FrameFocus.h | 12 ++++ src/Reset.cpp | 2 +- src/Source.cpp | 2 +- src/Url.cpp | 2 +- src/Visit.cpp | 2 +- src/WebPage.cpp | 18 ++++-- src/WebPage.h | 1 + src/find_command.h | 1 + src/webkit_server.pro | 4 +- 16 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 src/FrameFocus.cpp create mode 100644 src/FrameFocus.h diff --git a/lib/capybara/driver/webkit.rb b/lib/capybara/driver/webkit.rb index 55f9cee..67236b8 100644 --- a/lib/capybara/driver/webkit.rb +++ b/lib/capybara/driver/webkit.rb @@ -53,7 +53,9 @@ class Capybara::Driver::Webkit end def within_frame(frame_id) - raise Capybara::NotSupportedByDriverError + browser.frame_focus_id(frame_id) + yield + browser.frame_focus_parent end def within_window(handle) diff --git a/lib/capybara/driver/webkit/browser.rb b/lib/capybara/driver/webkit/browser.rb index add4daa..cd40e69 100644 --- a/lib/capybara/driver/webkit/browser.rb +++ b/lib/capybara/driver/webkit/browser.rb @@ -30,6 +30,14 @@ class Capybara::Driver::Webkit command("Url") end + def frame_focus_id(frame_id) + command("FrameFocus", frame_id) + end + + def frame_focus_parent + command("FrameFocus") + end + def command(name, *args) @socket.puts name @socket.puts args.size diff --git a/spec/driver_spec.rb b/spec/driver_spec.rb index 6ca3342..306d1c8 100644 --- a/spec/driver_spec.rb +++ b/spec/driver_spec.rb @@ -6,6 +6,89 @@ describe Capybara::Driver::Webkit do before { subject.visit("/hello/world?success=true") } after { subject.reset! } + context "iframe app" do + before(:all) do + @app = lambda do |env| + params = ::Rack::Utils.parse_query(env['QUERY_STRING']) + iframe = unless params["iframe"] + "" + end + if iframe + p_id = "farewell" + msg = "goodbye" + else + p_id = "greeting" + msg = "hello" + end + body = <<-HTML + + + + + +
+
Can't see me
+
+ + + + HTML + [200, + { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s }, + [body]] + end + end + + it "finds content after loading a URL" do + subject.within_frame("f") do + subject.find("//*[contains(., 'goodbye')]").should_not be_empty + end + end + + it "returns an attribute's value" do + subject.within_frame("f") do + subject.find("//p").first["id"].should == "farewell" + end + end + + it "returns a node's text" do + subject.within_frame("f") do + subject.find("//p").first.text.should == "goodbye" + end + end + + it "returns the current URL" do + subject.within_frame("f") do + port = subject.instance_variable_get("@rack_server").port + subject.current_url.should == "http://127.0.0.1:#{port}/hello/world?iframe=true" + end + end + + it "returns the source code for the page" do + subject.within_frame("f") do + subject.source.should =~ %r{.*farewell.*}m + end + end + + it "evaluates Javascript" do + subject.within_frame("f") do + result = subject.evaluate_script(%) + result.should == "hello" + end + end + + it "executes Javascript" do + subject.within_frame("f") do + subject.execute_script(%) + subject.find("//p[contains(., 'yo')]").should_not be_empty + end + end + end + context "hello app" do before(:all) do @app = lambda do |env| diff --git a/src/Connection.cpp b/src/Connection.cpp index 66e0eac..3733be6 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -9,6 +9,7 @@ #include "Source.h" #include "Evaluate.h" #include "Execute.h" +#include "FrameFocus.h" #include #include diff --git a/src/Evaluate.cpp b/src/Evaluate.cpp index 53699d5..695dabc 100644 --- a/src/Evaluate.cpp +++ b/src/Evaluate.cpp @@ -7,7 +7,7 @@ Evaluate::Evaluate(WebPage *page, QObject *parent) : Command(page, parent) { } void Evaluate::start(QStringList &arguments) { - QVariant result = page()->mainFrame()->evaluateJavaScript(arguments[0]); + QVariant result = page()->currentFrame()->evaluateJavaScript(arguments[0]); addVariant(result); emit finished(true, m_buffer); } diff --git a/src/Execute.cpp b/src/Execute.cpp index 06469e8..3a5410e 100644 --- a/src/Execute.cpp +++ b/src/Execute.cpp @@ -6,7 +6,7 @@ Execute::Execute(WebPage *page, QObject *parent) : Command(page, parent) { void Execute::start(QStringList &arguments) { QString script = arguments[0] + QString("; 'success'"); - QVariant result = page()->mainFrame()->evaluateJavaScript(script); + QVariant result = page()->currentFrame()->evaluateJavaScript(script); QString response; if (result.isValid()) { emit finished(true, response); diff --git a/src/FrameFocus.cpp b/src/FrameFocus.cpp new file mode 100644 index 0000000..39b439d --- /dev/null +++ b/src/FrameFocus.cpp @@ -0,0 +1,12 @@ +#include "FrameFocus.h" +#include "Command.h" +#include "WebPage.h" + +FrameFocus::FrameFocus(WebPage *page, QObject *parent) : Command(page, parent) { +} + +void FrameFocus::start(QStringList &arguments) { + QString response; + emit finished(true, response); +} + diff --git a/src/FrameFocus.h b/src/FrameFocus.h new file mode 100644 index 0000000..a54f429 --- /dev/null +++ b/src/FrameFocus.h @@ -0,0 +1,12 @@ +#include "Command.h" + +class WebPage; + +class FrameFocus : public Command { + Q_OBJECT + + public: + FrameFocus(WebPage *page, QObject *parent = 0); + virtual void start(QStringList &arguments); +}; + diff --git a/src/Reset.cpp b/src/Reset.cpp index d20662b..5759074 100644 --- a/src/Reset.cpp +++ b/src/Reset.cpp @@ -8,7 +8,7 @@ void Reset::start(QStringList &arguments) { Q_UNUSED(arguments); page()->triggerAction(QWebPage::Stop); - page()->mainFrame()->setHtml(""); + page()->currentFrame()->setHtml(""); page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); QString response = ""; emit finished(true, response); diff --git a/src/Source.cpp b/src/Source.cpp index 00b5240..c548923 100644 --- a/src/Source.cpp +++ b/src/Source.cpp @@ -7,7 +7,7 @@ Source::Source(WebPage *page, QObject *parent) : Command(page, parent) { void Source::start(QStringList &arguments) { Q_UNUSED(arguments) - QString response = page()->mainFrame()->toHtml(); + QString response = page()->currentFrame()->toHtml(); emit finished(true, response); } diff --git a/src/Url.cpp b/src/Url.cpp index b7f2d33..0a5b451 100644 --- a/src/Url.cpp +++ b/src/Url.cpp @@ -7,7 +7,7 @@ Url::Url(WebPage *page, QObject *parent) : Command(page, parent) { void Url::start(QStringList &argments) { Q_UNUSED(argments); - QUrl humanUrl = page()->mainFrame()->url(); + QUrl humanUrl = page()->currentFrame()->url(); QByteArray encodedBytes = humanUrl.toEncoded(); QString response = QString(encodedBytes); diff --git a/src/Visit.cpp b/src/Visit.cpp index cd15e66..9214211 100644 --- a/src/Visit.cpp +++ b/src/Visit.cpp @@ -7,7 +7,7 @@ Visit::Visit(WebPage *page, QObject *parent) : Command(page, parent) { } void Visit::start(QStringList &arguments) { - page()->mainFrame()->setUrl(QUrl(arguments[0])); + page()->currentFrame()->setUrl(QUrl(arguments[0])); } void Visit::loadFinished(bool success) { diff --git a/src/WebPage.cpp b/src/WebPage.cpp index d71f21c..2545f27 100644 --- a/src/WebPage.cpp +++ b/src/WebPage.cpp @@ -4,8 +4,6 @@ #include WebPage::WebPage(QObject *parent) : QWebPage(parent) { - connect(mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), - this, SLOT(injectJavascriptHelpers())); QResource javascript(":/capybara.js"); char * javascriptString = new char[javascript.size() + 1]; strcpy(javascriptString, (const char *)javascript.data()); @@ -14,10 +12,18 @@ WebPage::WebPage(QObject *parent) : QWebPage(parent) { m_loading = false; connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted())); connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); + connect(this, SIGNAL(frameCreated(QWebFrame *)), + this, SLOT(frameCreated(QWebFrame *))); +} + +void WebPage::frameCreated(QWebFrame * frame) { + connect(frame, SIGNAL(javaScriptWindowObjectCleared()), + this, SLOT(injectJavascriptHelpers())); } void WebPage::injectJavascriptHelpers() { - mainFrame()->evaluateJavaScript(m_capybaraJavascript); + QWebFrame* frame = qobject_cast(QObject::sender()); + frame->evaluateJavaScript(m_capybaraJavascript); } bool WebPage::shouldInterruptJavaScript() { @@ -28,9 +34,9 @@ QVariant WebPage::invokeCapybaraFunction(const char *name, QStringList &argument QString qname(name); QString objectName("CapybaraInvocation"); JavascriptInvocation invocation(qname, arguments); - mainFrame()->addToJavaScriptWindowObject(objectName, &invocation); + currentFrame()->addToJavaScriptWindowObject(objectName, &invocation); QString javascript = QString("Capybara.invoke()"); - return mainFrame()->evaluateJavaScript(javascript); + return currentFrame()->evaluateJavaScript(javascript); } QVariant WebPage::invokeCapybaraFunction(QString &name, QStringList &arguments) { @@ -76,6 +82,6 @@ bool WebPage::isLoading() const { } QString WebPage::failureString() { - return QString("Unable to load URL: ") + mainFrame()->url().toString(); + return QString("Unable to load URL: ") + currentFrame()->url().toString(); } diff --git a/src/WebPage.h b/src/WebPage.h index 8bbba10..1ad634a 100644 --- a/src/WebPage.h +++ b/src/WebPage.h @@ -15,6 +15,7 @@ class WebPage : public QWebPage { void loadStarted(); void loadFinished(bool); bool isLoading() const; + void frameCreated(QWebFrame *); protected: virtual void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID); diff --git a/src/find_command.h b/src/find_command.h index b38ad33..9744c1a 100644 --- a/src/find_command.h +++ b/src/find_command.h @@ -11,3 +11,4 @@ CHECK_COMMAND(Url) CHECK_COMMAND(Source) CHECK_COMMAND(Evaluate) CHECK_COMMAND(Execute) +CHECK_COMMAND(FrameFocus) diff --git a/src/webkit_server.pro b/src/webkit_server.pro index 8079223..1a72cae 100644 --- a/src/webkit_server.pro +++ b/src/webkit_server.pro @@ -1,8 +1,8 @@ TEMPLATE = app TARGET = webkit_server DESTDIR = . -HEADERS = WebPage.h Server.h Connection.h Command.h Visit.h Find.h Reset.h Node.h JavascriptInvocation.h Url.h Source.h Evaluate.h Execute.h -SOURCES = main.cpp WebPage.cpp Server.cpp Connection.cpp Command.cpp Visit.cpp Find.cpp Reset.cpp Node.cpp JavascriptInvocation.cpp Url.cpp Source.cpp Evaluate.cpp Execute.cpp +HEADERS = WebPage.h Server.h Connection.h Command.h Visit.h Find.h Reset.h Node.h JavascriptInvocation.h Url.h Source.h Evaluate.h Execute.h FrameFocus.h +SOURCES = main.cpp WebPage.cpp Server.cpp Connection.cpp Command.cpp Visit.cpp Find.cpp Reset.cpp Node.cpp JavascriptInvocation.cpp Url.cpp Source.cpp Evaluate.cpp Execute.cpp FrameFocus.cpp RESOURCES = webkit_server.qrc QT += network webkit CONFIG += console