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 "webkit/glue/resource_fetcher.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/message_loop.h"
11 #include "base/timer.h"
12 #include "content/public/common/content_switches.h"
13 #include "content/public/renderer/render_view.h"
14 #include "content/public/test/test_utils.h"
15 #include "content/shell/shell.h"
16 #include "content/test/content_browser_test.h"
17 #include "content/test/content_browser_test_utils.h"
18 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLResponse.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
22 using WebKit::WebFrame
;
23 using WebKit::WebURLRequest
;
24 using WebKit::WebURLResponse
;
25 using webkit_glue::ResourceFetcher
;
26 using webkit_glue::ResourceFetcherWithTimeout
;
30 static const int kMaxWaitTimeMs
= 5000;
32 class FetcherDelegate
{
37 // Start a repeating timer waiting for the download to complete. The
38 // callback has to be a static function, so we hold on to our instance.
39 FetcherDelegate::instance_
= this;
43 virtual ~FetcherDelegate() {}
45 ResourceFetcher::Callback
NewCallback() {
46 return base::Bind(&FetcherDelegate::OnURLFetchComplete
,
47 base::Unretained(this));
50 virtual void OnURLFetchComplete(const WebURLResponse
& response
,
51 const std::string
& data
) {
60 bool completed() const { return completed_
; }
61 bool timed_out() const { return timed_out_
; }
63 std::string
data() const { return data_
; }
64 const WebURLResponse
& response() const { return response_
; }
66 // Wait for the request to complete or timeout.
67 void WaitForResponse() {
68 scoped_refptr
<MessageLoopRunner
> runner
= new MessageLoopRunner
;
69 quit_task_
= runner
->QuitClosure();
74 timer_
.Start(FROM_HERE
,
75 base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs
),
77 &FetcherDelegate::TimerFired
);
81 ASSERT_FALSE(completed_
);
86 FAIL() << "fetch timed out";
89 static FetcherDelegate
* instance_
;
92 base::OneShotTimer
<FetcherDelegate
> timer_
;
95 WebURLResponse response_
;
97 base::Closure quit_task_
;
100 FetcherDelegate
* FetcherDelegate::instance_
= NULL
;
102 class EvilFetcherDelegate
: public FetcherDelegate
{
104 virtual ~EvilFetcherDelegate() {}
106 void SetFetcher(ResourceFetcher
* fetcher
) {
107 fetcher_
.reset(fetcher
);
110 virtual void OnURLFetchComplete(const WebURLResponse
& response
,
111 const std::string
& data
) OVERRIDE
{
112 // Destroy the ResourceFetcher here. We are testing that upon returning
113 // to the ResourceFetcher that it does not crash.
115 FetcherDelegate::OnURLFetchComplete(response
, data
);
119 scoped_ptr
<ResourceFetcher
> fetcher_
;
122 class ResourceFetcherTests
: public ContentBrowserTest
{
124 virtual void SetUpCommandLine(CommandLine
* command_line
) {
125 command_line
->AppendSwitch(switches::kSingleProcess
);
126 #if defined(OS_WIN) && defined(USE_AURA)
127 // Don't want to try to create a GPU process.
128 command_line
->AppendSwitch(switches::kDisableAcceleratedCompositing
);
132 RenderView
* GetRenderView() {
133 // We could have the test on the UI thread get the WebContent's routing ID,
134 // but we know this will be the first RV so skip that and just hardcode it.
135 return RenderView::FromRoutingID(1);
138 void ResourceFetcherDownloadOnRenderer(const GURL
& url
) {
139 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
141 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
142 scoped_ptr
<ResourceFetcher
> fetcher(new ResourceFetcher(
143 url
, frame
, WebURLRequest::TargetIsMainFrame
, delegate
->NewCallback()));
145 delegate
->WaitForResponse();
147 ASSERT_TRUE(delegate
->completed());
148 EXPECT_EQ(delegate
->response().httpStatusCode(), 200);
149 std::string text
= delegate
->data();
150 EXPECT_TRUE(text
.find("Basic html test.") != std::string::npos
);
153 void ResourceFetcher404OnRenderer(const GURL
& url
) {
154 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
156 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
157 scoped_ptr
<ResourceFetcher
> fetcher(new ResourceFetcher(
158 url
, frame
, WebURLRequest::TargetIsMainFrame
, delegate
->NewCallback()));
160 delegate
->WaitForResponse();
162 ASSERT_TRUE(delegate
->completed());
163 EXPECT_EQ(delegate
->response().httpStatusCode(), 404);
164 EXPECT_TRUE(delegate
->data().find("Not Found.") != std::string::npos
);
167 void ResourceFetcherDidFailOnRenderer() {
168 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
170 // Try to fetch a page on a site that doesn't exist.
171 GURL
url("http://localhost:1339/doesnotexist");
172 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
173 scoped_ptr
<ResourceFetcher
> fetcher(new ResourceFetcher(
174 url
, frame
, WebURLRequest::TargetIsMainFrame
, delegate
->NewCallback()));
176 delegate
->WaitForResponse();
178 // When we fail, we still call the Delegate callback but we pass in empty
180 EXPECT_TRUE(delegate
->completed());
181 EXPECT_TRUE(delegate
->response().isNull());
182 EXPECT_EQ(delegate
->data(), std::string());
183 EXPECT_FALSE(delegate
->timed_out());
186 void ResourceFetcherTimeoutOnRenderer(const GURL
& url
) {
187 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
189 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
190 scoped_ptr
<ResourceFetcher
> fetcher(new ResourceFetcherWithTimeout(
191 url
, frame
, WebURLRequest::TargetIsMainFrame
,
192 0, delegate
->NewCallback()));
194 delegate
->WaitForResponse();
196 // When we timeout, we still call the Delegate callback but we pass in empty
198 EXPECT_TRUE(delegate
->completed());
199 EXPECT_TRUE(delegate
->response().isNull());
200 EXPECT_EQ(delegate
->data(), std::string());
201 EXPECT_FALSE(delegate
->timed_out());
204 void ResourceFetcherDeletedInCallbackOnRenderer(const GURL
& url
) {
205 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
207 scoped_ptr
<EvilFetcherDelegate
> delegate(new EvilFetcherDelegate
);
208 scoped_ptr
<ResourceFetcher
> fetcher(new ResourceFetcherWithTimeout(
209 url
, frame
, WebURLRequest::TargetIsMainFrame
,
210 0, delegate
->NewCallback()));
211 delegate
->SetFetcher(fetcher
.release());
213 delegate
->WaitForResponse();
214 EXPECT_FALSE(delegate
->timed_out());
218 // Test a fetch from the test server.
219 // If this flakes, use http://crbug.com/51622.
220 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherDownload
) {
221 // Need to spin up the renderer.
222 NavigateToURL(shell(), GURL("about:blank"));
224 ASSERT_TRUE(test_server()->Start());
225 GURL
url(test_server()->GetURL("files/simple_page.html"));
227 PostTaskToInProcessRendererAndWait(
228 base::Bind(&ResourceFetcherTests::ResourceFetcherDownloadOnRenderer
,
229 base::Unretained(this), url
));
232 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcher404
) {
233 // Need to spin up the renderer.
234 NavigateToURL(shell(), GURL("about:blank"));
236 // Test 404 response.
237 ASSERT_TRUE(test_server()->Start());
238 GURL url
= test_server()->GetURL("files/thisfiledoesntexist.html");
240 PostTaskToInProcessRendererAndWait(
241 base::Bind(&ResourceFetcherTests::ResourceFetcher404OnRenderer
,
242 base::Unretained(this), url
));
245 // If this flakes, use http://crbug.com/51622.
246 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherDidFail
) {
247 // Need to spin up the renderer.
248 NavigateToURL(shell(), GURL("about:blank"));
250 PostTaskToInProcessRendererAndWait(
251 base::Bind(&ResourceFetcherTests::ResourceFetcherDidFailOnRenderer
,
252 base::Unretained(this)));
255 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherTimeout
) {
256 // Need to spin up the renderer.
257 NavigateToURL(shell(), GURL("about:blank"));
259 // Grab a page that takes at least 1 sec to respond, but set the fetcher to
261 ASSERT_TRUE(test_server()->Start());
262 GURL
url(test_server()->GetURL("slow?1"));
264 PostTaskToInProcessRendererAndWait(
265 base::Bind(&ResourceFetcherTests::ResourceFetcherTimeoutOnRenderer
,
266 base::Unretained(this), url
));
269 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherDeletedInCallback
) {
270 // Need to spin up the renderer.
271 NavigateToURL(shell(), GURL("about:blank"));
273 // Grab a page that takes at least 1 sec to respond, but set the fetcher to
275 ASSERT_TRUE(test_server()->Start());
276 GURL
url(test_server()->GetURL("slow?1"));
278 PostTaskToInProcessRendererAndWait(
280 &ResourceFetcherTests::ResourceFetcherDeletedInCallbackOnRenderer
,
281 base::Unretained(this), url
));
284 } // namespace content