1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/fetchers/resource_fetcher.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/timer/timer.h"
12 #include "content/public/common/content_switches.h"
13 #include "content/public/common/url_constants.h"
14 #include "content/public/renderer/render_view.h"
15 #include "content/public/test/test_utils.h"
16 #include "content/shell/browser/shell.h"
17 #include "content/test/content_browser_test.h"
18 #include "content/test/content_browser_test_utils.h"
19 #include "third_party/WebKit/public/platform/WebURLResponse.h"
20 #include "third_party/WebKit/public/web/WebFrame.h"
21 #include "third_party/WebKit/public/web/WebView.h"
23 using WebKit::WebFrame;
24 using WebKit::WebURLRequest;
25 using WebKit::WebURLResponse;
29 static const int kMaxWaitTimeMs = 5000;
31 class FetcherDelegate {
36 // Start a repeating timer waiting for the download to complete. The
37 // callback has to be a static function, so we hold on to our instance.
38 FetcherDelegate::instance_ = this;
42 virtual ~FetcherDelegate() {}
44 ResourceFetcher::Callback NewCallback() {
45 return base::Bind(&FetcherDelegate::OnURLFetchComplete,
46 base::Unretained(this));
49 virtual void OnURLFetchComplete(const WebURLResponse& response,
50 const std::string& data) {
59 bool completed() const { return completed_; }
60 bool timed_out() const { return timed_out_; }
62 std::string data() const { return data_; }
63 const WebURLResponse& response() const { return response_; }
65 // Wait for the request to complete or timeout.
66 void WaitForResponse() {
67 scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner;
68 quit_task_ = runner->QuitClosure();
73 timer_.Start(FROM_HERE,
74 base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs),
76 &FetcherDelegate::TimerFired);
80 ASSERT_FALSE(completed_);
85 FAIL() << "fetch timed out";
88 static FetcherDelegate* instance_;
91 base::OneShotTimer<FetcherDelegate> timer_;
94 WebURLResponse response_;
96 base::Closure quit_task_;
99 FetcherDelegate* FetcherDelegate::instance_ = NULL;
101 class EvilFetcherDelegate : public FetcherDelegate {
103 virtual ~EvilFetcherDelegate() {}
105 void SetFetcher(ResourceFetcher* fetcher) {
106 fetcher_.reset(fetcher);
109 virtual void OnURLFetchComplete(const WebURLResponse& response,
110 const std::string& data) OVERRIDE {
111 // Destroy the ResourceFetcher here. We are testing that upon returning
112 // to the ResourceFetcher that it does not crash.
114 FetcherDelegate::OnURLFetchComplete(response, data);
118 scoped_ptr<ResourceFetcher> fetcher_;
121 class ResourceFetcherTests : public ContentBrowserTest {
123 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
124 command_line->AppendSwitch(switches::kSingleProcess);
125 #if defined(OS_WIN) && defined(USE_AURA)
126 // Don't want to try to create a GPU process.
127 command_line->AppendSwitch(switches::kDisableAcceleratedCompositing);
131 RenderView* GetRenderView() {
132 // We could have the test on the UI thread get the WebContent's routing ID,
133 // but we know this will be the first RV so skip that and just hardcode it.
134 return RenderView::FromRoutingID(1);
137 void ResourceFetcherDownloadOnRenderer(const GURL& url) {
138 WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
140 scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
141 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
142 url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback()));
144 delegate->WaitForResponse();
146 ASSERT_TRUE(delegate->completed());
147 EXPECT_EQ(delegate->response().httpStatusCode(), 200);
148 std::string text = delegate->data();
149 EXPECT_TRUE(text.find("Basic html test.") != std::string::npos);
152 void ResourceFetcher404OnRenderer(const GURL& url) {
153 WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
155 scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
156 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
157 url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback()));
159 delegate->WaitForResponse();
161 ASSERT_TRUE(delegate->completed());
162 EXPECT_EQ(delegate->response().httpStatusCode(), 404);
163 EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos);
166 void ResourceFetcherDidFailOnRenderer() {
167 WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
169 // Try to fetch a page on a site that doesn't exist.
170 GURL url("http://localhost:1339/doesnotexist");
171 scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
172 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
173 url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback()));
175 delegate->WaitForResponse();
177 // When we fail, we still call the Delegate callback but we pass in empty
179 EXPECT_TRUE(delegate->completed());
180 EXPECT_TRUE(delegate->response().isNull());
181 EXPECT_EQ(delegate->data(), std::string());
182 EXPECT_FALSE(delegate->timed_out());
185 void ResourceFetcherTimeoutOnRenderer(const GURL& url) {
186 WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
188 scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
189 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
190 url, frame, WebURLRequest::TargetIsMainFrame,
191 0, delegate->NewCallback()));
193 delegate->WaitForResponse();
195 // When we timeout, we still call the Delegate callback but we pass in empty
197 EXPECT_TRUE(delegate->completed());
198 EXPECT_TRUE(delegate->response().isNull());
199 EXPECT_EQ(delegate->data(), std::string());
200 EXPECT_FALSE(delegate->timed_out());
203 void ResourceFetcherDeletedInCallbackOnRenderer(const GURL& url) {
204 WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
206 scoped_ptr<EvilFetcherDelegate> delegate(new EvilFetcherDelegate);
207 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
208 url, frame, WebURLRequest::TargetIsMainFrame,
209 0, delegate->NewCallback()));
210 delegate->SetFetcher(fetcher.release());
212 delegate->WaitForResponse();
213 EXPECT_FALSE(delegate->timed_out());
217 // Test a fetch from the test server.
218 // If this flakes, use http://crbug.com/51622.
219 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDownload) {
220 // Need to spin up the renderer.
221 NavigateToURL(shell(), GURL(kAboutBlankURL));
223 ASSERT_TRUE(test_server()->Start());
224 GURL url(test_server()->GetURL("files/simple_page.html"));
226 PostTaskToInProcessRendererAndWait(
227 base::Bind(&ResourceFetcherTests::ResourceFetcherDownloadOnRenderer,
228 base::Unretained(this), url));
231 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcher404) {
232 // Need to spin up the renderer.
233 NavigateToURL(shell(), GURL(kAboutBlankURL));
235 // Test 404 response.
236 ASSERT_TRUE(test_server()->Start());
237 GURL url = test_server()->GetURL("files/thisfiledoesntexist.html");
239 PostTaskToInProcessRendererAndWait(
240 base::Bind(&ResourceFetcherTests::ResourceFetcher404OnRenderer,
241 base::Unretained(this), url));
244 // If this flakes, use http://crbug.com/51622.
245 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDidFail) {
246 // Need to spin up the renderer.
247 NavigateToURL(shell(), GURL(kAboutBlankURL));
249 PostTaskToInProcessRendererAndWait(
250 base::Bind(&ResourceFetcherTests::ResourceFetcherDidFailOnRenderer,
251 base::Unretained(this)));
254 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) {
255 // Need to spin up the renderer.
256 NavigateToURL(shell(), GURL(kAboutBlankURL));
258 // Grab a page that takes at least 1 sec to respond, but set the fetcher to
260 ASSERT_TRUE(test_server()->Start());
261 GURL url(test_server()->GetURL("slow?1"));
263 PostTaskToInProcessRendererAndWait(
264 base::Bind(&ResourceFetcherTests::ResourceFetcherTimeoutOnRenderer,
265 base::Unretained(this), url));
268 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDeletedInCallback) {
269 // Need to spin up the renderer.
270 NavigateToURL(shell(), GURL(kAboutBlankURL));
272 // Grab a page that takes at least 1 sec to respond, but set the fetcher to
274 ASSERT_TRUE(test_server()->Start());
275 GURL url(test_server()->GetURL("slow?1"));
277 PostTaskToInProcessRendererAndWait(
279 &ResourceFetcherTests::ResourceFetcherDeletedInCallbackOnRenderer,
280 base::Unretained(this), url));
283 } // namespace content