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: 47 TestShellContentRendererClient() 48 : latest_error_valid_(false), 49 latest_error_reason_(0), 50 latest_error_stale_copy_in_cache_(false) {} 51 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 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. 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 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. 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. 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: 140 RenderViewBrowserTest() {} 141 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 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|. 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. 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. 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 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