1 // Copyright 2014 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.
4
5 // Browser tests targeted at the RenderView that run in browser context.
6 // Note that these tests rely on single-process mode, and hence may be
7 // disabled in some configurations (check gyp files).
8
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/common/content_switches.h"
20 #include "content/public/renderer/render_view.h"
21 #include "content/public/test/browser_test_utils.h"
22 #include "content/public/test/content_browser_test.h"
23 #include "content/public/test/content_browser_test_utils.h"
24 #include "content/public/test/test_utils.h"
25 #include "content/shell/browser/shell.h"
26 #include "content/shell/browser/shell_browser_context.h"
27 #include "content/shell/browser/shell_content_browser_client.h"
28 #include "content/shell/common/shell_content_client.h"
29 #include "content/shell/renderer/shell_content_renderer_client.h"
30 #include "net/base/net_errors.h"
31 #include "net/disk_cache/disk_cache.h"
32 #include "net/http/failing_http_transaction_factory.h"
33 #include "net/http/http_cache.h"
34 #include "net/url_request/url_request_context.h"
35 #include "net/url_request/url_request_context_getter.h"
36 #include "testing/gtest/include/gtest/gtest.h"
37 #include "third_party/WebKit/public/platform/WebURLError.h"
38 #include "third_party/WebKit/public/platform/WebURLRequest.h"
39 #include "third_party/WebKit/public/web/WebFrame.h"
40
41 namespace content {
42
43 namespace {
44
45 class TestShellContentRendererClient : public ShellContentRendererClient {
46 public:
TestShellContentRendererClient()47 TestShellContentRendererClient()
48 : latest_error_valid_(false),
49 latest_error_reason_(0),
50 latest_error_stale_copy_in_cache_(false) {}
51
GetNavigationErrorStrings(content::RenderView * render_view,blink::WebFrame * frame,const blink::WebURLRequest & failed_request,const blink::WebURLError & error,std::string * error_html,base::string16 * error_description)52 virtual void GetNavigationErrorStrings(
53 content::RenderView* render_view,
54 blink::WebFrame* frame,
55 const blink::WebURLRequest& failed_request,
56 const blink::WebURLError& error,
57 std::string* error_html,
58 base::string16* error_description) OVERRIDE {
59 if (error_html)
60 *error_html = "A suffusion of yellow.";
61 latest_error_valid_ = true;
62 latest_error_reason_ = error.reason;
63 latest_error_stale_copy_in_cache_ = error.staleCopyInCache;
64 }
65
GetLatestError(int * error_code,bool * stale_cache_entry_present)66 bool GetLatestError(int* error_code, bool* stale_cache_entry_present) {
67 if (latest_error_valid_) {
68 *error_code = latest_error_reason_;
69 *stale_cache_entry_present = latest_error_stale_copy_in_cache_;
70 }
71 return latest_error_valid_;
72 }
73
74 private:
75 bool latest_error_valid_;
76 int latest_error_reason_;
77 bool latest_error_stale_copy_in_cache_;
78 };
79
80 // Must be called on IO thread.
InterceptNetworkTransactions(net::URLRequestContextGetter * getter,net::Error error)81 void InterceptNetworkTransactions(net::URLRequestContextGetter* getter,
82 net::Error error) {
83 DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::IO));
84 net::HttpCache* cache(
85 getter->GetURLRequestContext()->http_transaction_factory()->GetCache());
86 DCHECK(cache);
87 scoped_ptr<net::FailingHttpTransactionFactory> factory(
88 new net::FailingHttpTransactionFactory(cache->GetSession(), error));
89 // Throw away old version; since this is a browser test, there is no
90 // need to restore the old state.
91 cache->SetHttpNetworkTransactionFactoryForTesting(
92 factory.PassAs<net::HttpTransactionFactory>());
93 }
94
CallOnUIThreadValidatingReturn(const base::Closure & callback,int rv)95 void CallOnUIThreadValidatingReturn(const base::Closure& callback,
96 int rv) {
97 DCHECK_EQ(net::OK, rv);
98 BrowserThread::PostTask(
99 BrowserThread::UI, FROM_HERE, callback);
100 }
101
102 // Must be called on IO thread. The callback will be called on
103 // completion of cache clearing on the UI thread.
BackendClearCache(scoped_ptr<disk_cache::Backend * > backend,const base::Closure & callback,int rv)104 void BackendClearCache(scoped_ptr<disk_cache::Backend*> backend,
105 const base::Closure& callback,
106 int rv) {
107 DCHECK(*backend);
108 DCHECK_EQ(net::OK, rv);
109 (*backend)->DoomAllEntries(
110 base::Bind(&CallOnUIThreadValidatingReturn, callback));
111 }
112
113 // Must be called on IO thread. The callback will be called on
114 // completion of cache clearing on the UI thread.
ClearCache(net::URLRequestContextGetter * getter,const base::Closure & callback)115 void ClearCache(net::URLRequestContextGetter* getter,
116 const base::Closure& callback) {
117 DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::IO));
118 net::HttpCache* cache(
119 getter->GetURLRequestContext()->http_transaction_factory()->GetCache());
120 DCHECK(cache);
121 scoped_ptr<disk_cache::Backend*> backend(new disk_cache::Backend*);
122 *backend = NULL;
123 disk_cache::Backend** backend_ptr = backend.get();
124
125 net::CompletionCallback backend_callback(
126 base::Bind(&BackendClearCache, base::Passed(backend.Pass()), callback));
127
128 // backend_ptr is valid until all copies of backend_callback go out
129 // of scope.
130 if (net::OK == cache->GetBackend(backend_ptr, backend_callback)) {
131 // The call completed synchronously, so GetBackend didn't run the callback.
132 backend_callback.Run(net::OK);
133 }
134 }
135
136 } // namespace
137
138 class RenderViewBrowserTest : public ContentBrowserTest {
139 public:
RenderViewBrowserTest()140 RenderViewBrowserTest() {}
141
SetUpCommandLine(CommandLine * command_line)142 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
143 // This method is needed to allow interaction with in-process renderer
144 // and use of a test ContentRendererClient.
145 command_line->AppendSwitch(switches::kSingleProcess);
146 }
147
SetUpOnMainThread()148 virtual void SetUpOnMainThread() OVERRIDE {
149 // Override setting of renderer client.
150 renderer_client_ = new TestShellContentRendererClient();
151 // Explicitly leaks ownership; this object will remain alive
152 // until process death. We don't deleted the returned value,
153 // since some contexts set the pointer to a non-heap address.
154 SetRendererClientForTesting(renderer_client_);
155 }
156
157 // Navigates to the given URL and waits for |num_navigations| to occur, and
158 // the title to change to |expected_title|.
NavigateToURLAndWaitForTitle(const GURL & url,const std::string & expected_title,int num_navigations)159 void NavigateToURLAndWaitForTitle(const GURL& url,
160 const std::string& expected_title,
161 int num_navigations) {
162 content::TitleWatcher title_watcher(
163 shell()->web_contents(), base::ASCIIToUTF16(expected_title));
164
165 content::NavigateToURLBlockUntilNavigationsComplete(
166 shell(), url, num_navigations);
167
168 EXPECT_EQ(base::ASCIIToUTF16(expected_title),
169 title_watcher.WaitAndGetTitle());
170 }
171
172 // Returns true if there is a valid error stored; in this case
173 // |*error_code| and |*stale_cache_entry_present| will be updated
174 // appropriately.
175 // Must be called after the renderer thread is created.
GetLatestErrorFromRendererClient(int * error_code,bool * stale_cache_entry_present)176 bool GetLatestErrorFromRendererClient(
177 int* error_code, bool* stale_cache_entry_present) {
178 bool result = false;
179
180 PostTaskToInProcessRendererAndWait(
181 base::Bind(&RenderViewBrowserTest::GetLatestErrorFromRendererClient0,
182 renderer_client_, &result, error_code,
183 stale_cache_entry_present));
184 return result;
185 }
186
187 private:
188 // Must be run on renderer thread.
GetLatestErrorFromRendererClient0(TestShellContentRendererClient * renderer_client,bool * result,int * error_code,bool * stale_cache_entry_present)189 static void GetLatestErrorFromRendererClient0(
190 TestShellContentRendererClient* renderer_client,
191 bool* result, int* error_code, bool* stale_cache_entry_present) {
192 *result = renderer_client->GetLatestError(
193 error_code, stale_cache_entry_present);
194 }
195
196 TestShellContentRendererClient* renderer_client_;
197 };
198
IN_PROC_BROWSER_TEST_F(RenderViewBrowserTest,ConfirmCacheInformationPlumbed)199 IN_PROC_BROWSER_TEST_F(RenderViewBrowserTest, ConfirmCacheInformationPlumbed) {
200 ASSERT_TRUE(test_server()->Start());
201
202 // Load URL with "nocache" set, to create stale cache.
203 GURL test_url(test_server()->GetURL("files/nocache.html"));
204 NavigateToURLAndWaitForTitle(test_url, "Nocache Test Page", 1);
205
206 // Reload same URL after forcing an error from the the network layer;
207 // confirm that the error page is told the cached copy exists.
208 int renderer_id =
209 shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
210 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
211 ShellContentBrowserClient::Get()->browser_context()->
212 GetRequestContextForRenderProcess(renderer_id);
213 BrowserThread::PostTask(
214 BrowserThread::IO, FROM_HERE,
215 base::Bind(&InterceptNetworkTransactions, url_request_context_getter,
216 net::ERR_FAILED));
217
218 // An error results in one completed navigation.
219 NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
220 int error_code = net::OK;
221 bool stale_cache_entry_present = false;
222 ASSERT_TRUE(GetLatestErrorFromRendererClient(
223 &error_code, &stale_cache_entry_present));
224 EXPECT_EQ(net::ERR_FAILED, error_code);
225 EXPECT_TRUE(stale_cache_entry_present);
226
227 // Clear the cache and repeat; confirm lack of entry in cache reported.
228 scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner;
229 BrowserThread::PostTask(
230 BrowserThread::IO, FROM_HERE,
231 base::Bind(&ClearCache, url_request_context_getter,
232 runner->QuitClosure()));
233 runner->Run();
234
235 content::NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
236
237 error_code = net::OK;
238 stale_cache_entry_present = true;
239 ASSERT_TRUE(GetLatestErrorFromRendererClient(
240 &error_code, &stale_cache_entry_present));
241 EXPECT_EQ(net::ERR_FAILED, error_code);
242 EXPECT_FALSE(stale_cache_entry_present);
243 }
244
245 } // namespace content
246