Fix status code and headers for iframes

QWebFrame::url() does not return a valid URL for iframes.  We can't just
look up the requested URL in m_responses because the request may have
been redirected, so instead we keep track of redirects and set up the
NetworkResponse when the final reply is received.
This commit is contained in:
Matthew Horan 2012-11-22 00:21:40 -05:00
parent e80a9c8b92
commit 5127c01366
10 changed files with 75 additions and 34 deletions

View File

@ -9,15 +9,29 @@ describe Capybara::Webkit::Driver do
let(:driver) do let(:driver) do
driver_for_app do driver_for_app do
get "/" do get "/" do
if in_iframe_request? if params[:iframe] == "true"
p_id = "farewell" redirect '/iframe'
msg = "goodbye"
iframe = nil
else else
p_id = "greeting" <<-HTML
msg = "hello" <html>
iframe = "<iframe id=\"f\" src=\"/?iframe=true\"></iframe>" <head>
<style type="text/css">
#display_none { display: none }
</style>
</head>
<body>
<iframe id="f" src="/?iframe=true"></iframe>
<script type="text/javascript">
document.write("<p id='greeting'>hello</p>");
</script>
</body>
</html>
HTML
end end
end
get '/iframe' do
headers 'X-Redirected' => 'true'
<<-HTML <<-HTML
<html> <html>
<head> <head>
@ -26,18 +40,13 @@ describe Capybara::Webkit::Driver do
</style> </style>
</head> </head>
<body> <body>
#{iframe}
<script type="text/javascript"> <script type="text/javascript">
document.write("<p id='#{p_id}'>#{msg}</p>"); document.write("<p id='farewell'>goodbye</p>");
</script> </script>
</body> </body>
</html> </html>
HTML HTML
end end
def in_iframe_request?
params[:iframe] == "true"
end
end end
end end
@ -90,13 +99,7 @@ describe Capybara::Webkit::Driver do
it "returns the current URL" do it "returns the current URL" do
driver.within_frame("f") do driver.within_frame("f") do
driver.current_url.should == driver_url(driver, "/?iframe=true") driver.current_url.should == driver_url(driver, "/iframe")
end
end
it "returns the source code for the page" do
driver.within_frame("f") do
driver.source.should =~ %r{<html>.*farewell.*}m
end end
end end
@ -121,6 +124,18 @@ describe Capybara::Webkit::Driver do
driver.current_url.should == original_url driver.current_url.should == original_url
end end
it "returns the headers for the page" do
driver.within_frame("f") do
driver.response_headers['X-Redirected'].should == "true"
end
end
it "returns the status code for the page" do
driver.within_frame("f") do
driver.status_code.should == 200
end
end
end end
context "error iframe app" do context "error iframe app" do
@ -150,8 +165,10 @@ describe Capybara::Webkit::Driver do
context "redirect app" do context "redirect app" do
let(:driver) do let(:driver) do
driver_for_app do driver_for_app do
enable :sessions
get '/target' do get '/target' do
headers 'X-Redirected' => 'true' headers 'X-Redirected' => (session.delete(:redirected) || false).to_s
"<p>#{env['CONTENT_TYPE']}</p>" "<p>#{env['CONTENT_TYPE']}</p>"
end end
@ -172,7 +189,12 @@ describe Capybara::Webkit::Driver do
end end
get '/redirect-me' do get '/redirect-me' do
redirect '/target' if session[:redirected]
redirect '/target'
else
session[:redirected] = true
redirect '/redirect-me'
end
end end
end end
end end
@ -200,13 +222,16 @@ describe Capybara::Webkit::Driver do
it "should make headers available through response_headers" do it "should make headers available through response_headers" do
driver.visit('/redirect-me') driver.visit('/redirect-me')
driver.response_headers['X-Redirected'].should == "true" driver.response_headers['X-Redirected'].should == "true"
driver.visit('/target')
driver.response_headers['X-Redirected'].should == "false"
end end
it "should make the status code available through status_code" do it "should make the status code available through status_code" do
driver.visit('/redirect-me') driver.visit('/redirect-me')
driver.status_code.should == 200 driver.status_code.should == 200
driver.visit('/target')
driver.status_code.should == 200
end end
end end
context "css app" do context "css app" do

View File

@ -9,7 +9,7 @@ void Authenticate::start() {
QString username = arguments()[0]; QString username = arguments()[0];
QString password = arguments()[1]; QString password = arguments()[1];
NetworkAccessManager* networkAccessManager = qobject_cast<NetworkAccessManager*>(page()->networkAccessManager()); NetworkAccessManager* networkAccessManager = page()->networkAccessManager();
networkAccessManager->setUserName(username); networkAccessManager->setUserName(username);
networkAccessManager->setPassword(password); networkAccessManager->setPassword(password);

View File

@ -9,7 +9,7 @@ Header::Header(WebPageManager *manager, QStringList &arguments, QObject *parent)
void Header::start() { void Header::start() {
QString key = arguments()[0]; QString key = arguments()[0];
QString value = arguments()[1]; QString value = arguments()[1];
NetworkAccessManager* networkAccessManager = qobject_cast<NetworkAccessManager*>(page()->networkAccessManager()); NetworkAccessManager* networkAccessManager = page()->networkAccessManager();
if (key.toLower().replace("-", "_") == "user_agent") { if (key.toLower().replace("-", "_") == "user_agent") {
page()->setUserAgent(value); page()->setUserAgent(value);
} else { } else {

View File

@ -30,10 +30,18 @@ QNetworkReply* NetworkAccessManager::createRequest(QNetworkAccessManager::Operat
}; };
void NetworkAccessManager::finished(QNetworkReply *reply) { void NetworkAccessManager::finished(QNetworkReply *reply) {
NetworkResponse response; QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
response.statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (redirectUrl.isValid())
response.headers = reply->rawHeaderPairs(); m_redirectMappings[reply->url().resolved(redirectUrl)] = reply->url();
m_responses[reply->url()] = response; else {
QUrl requestedUrl = reply->url();
while (m_redirectMappings.contains(requestedUrl))
requestedUrl = m_redirectMappings.take(requestedUrl);
NetworkResponse response;
response.statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
response.headers = reply->rawHeaderPairs();
m_responses[requestedUrl] = response;
}
} }
void NetworkAccessManager::addHeader(QString key, QString value) { void NetworkAccessManager::addHeader(QString key, QString value) {

View File

@ -35,6 +35,7 @@ class NetworkAccessManager : public QNetworkAccessManager {
QHash<QUrl, NetworkResponse> m_responses; QHash<QUrl, NetworkResponse> m_responses;
bool isBlacklisted(QUrl url); bool isBlacklisted(QUrl url);
QNetworkReply* noOpRequest(); QNetworkReply* noOpRequest();
QHash<QUrl, QUrl> m_redirectMappings;
private slots: private slots:
void provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator); void provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator);

View File

@ -1,7 +1,7 @@
#include "SetProxy.h" #include "SetProxy.h"
#include "WebPage.h" #include "WebPage.h"
#include "WebPageManager.h" #include "WebPageManager.h"
#include <QNetworkAccessManager> #include "NetworkAccessManager.h"
#include <QNetworkProxy> #include <QNetworkProxy>
SetProxy::SetProxy(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {} SetProxy::SetProxy(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}

View File

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

View File

@ -1,12 +1,13 @@
#include "Source.h" #include "Source.h"
#include "WebPage.h" #include "WebPage.h"
#include "WebPageManager.h" #include "WebPageManager.h"
#include "NetworkAccessManager.h"
Source::Source(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) { Source::Source(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
} }
void Source::start() { void Source::start() {
QNetworkAccessManager* accessManager = page()->networkAccessManager(); NetworkAccessManager* accessManager = page()->networkAccessManager();
QNetworkRequest request(page()->currentFrame()->requestedUrl()); QNetworkRequest request(page()->currentFrame()->requestedUrl());
reply = accessManager->get(request); reply = accessManager->get(request);

View File

@ -253,11 +253,15 @@ void WebPage::setSkipImageLoading(bool skip) {
} }
int WebPage::getLastStatus() { int WebPage::getLastStatus() {
return qobject_cast<NetworkAccessManager *>(networkAccessManager())->statusFor(currentFrame()->url()); return networkAccessManager()->statusFor(currentFrame()->requestedUrl());
} }
const QList<QNetworkReply::RawHeaderPair> &WebPage::pageHeaders() { const QList<QNetworkReply::RawHeaderPair> &WebPage::pageHeaders() {
return qobject_cast<NetworkAccessManager *>(networkAccessManager())->headersFor(currentFrame()->url()); return networkAccessManager()->headersFor(currentFrame()->requestedUrl());
}
NetworkAccessManager *WebPage::networkAccessManager() {
return qobject_cast<NetworkAccessManager *>(QWebPage::networkAccessManager());
} }
void WebPage::handleUnsupportedContent(QNetworkReply *reply) { void WebPage::handleUnsupportedContent(QNetworkReply *reply) {

View File

@ -3,6 +3,7 @@
#include <QtWebKit> #include <QtWebKit>
class WebPageManager; class WebPageManager;
class NetworkAccessManager;
class WebPage : public QWebPage { class WebPage : public QWebPage {
Q_OBJECT Q_OBJECT
@ -32,6 +33,7 @@ class WebPage : public QWebPage {
QString getWindowName(); QString getWindowName();
bool matchesWindowSelector(QString); bool matchesWindowSelector(QString);
void setFocus(); void setFocus();
NetworkAccessManager *networkAccessManager();
public slots: public slots:
bool shouldInterruptJavaScript(); bool shouldInterruptJavaScript();