| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Browser tests targeted at the RenderView that run in browser context. |
| // Note that these tests rely on single-process mode, and hence may be |
| // disabled in some configurations (check gyp files). |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/renderer/render_view.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| #include "content/shell/browser/shell.h" |
| #include "content/shell/browser/shell_browser_context.h" |
| #include "content/shell/browser/shell_content_browser_client.h" |
| #include "content/shell/common/shell_content_client.h" |
| #include "content/shell/renderer/shell_content_renderer_client.h" |
| #include "net/base/net_errors.h" |
| #include "net/disk_cache/disk_cache.h" |
| #include "net/http/failing_http_transaction_factory.h" |
| #include "net/http/http_cache.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/WebKit/public/platform/WebURLError.h" |
| #include "third_party/WebKit/public/platform/WebURLRequest.h" |
| #include "third_party/WebKit/public/web/WebFrame.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| class TestShellContentRendererClient : public ShellContentRendererClient { |
| public: |
| TestShellContentRendererClient() |
| : latest_error_valid_(false), |
| latest_error_reason_(0), |
| latest_error_stale_copy_in_cache_(false) {} |
| |
| void GetNavigationErrorStrings(content::RenderFrame* render_frame, |
| const blink::WebURLRequest& failed_request, |
| const blink::WebURLError& error, |
| std::string* error_html, |
| base::string16* error_description) override { |
| if (error_html) |
| *error_html = "A suffusion of yellow."; |
| latest_error_valid_ = true; |
| latest_error_reason_ = error.reason; |
| latest_error_stale_copy_in_cache_ = error.staleCopyInCache; |
| } |
| |
| bool GetLatestError(int* error_code, bool* stale_cache_entry_present) { |
| if (latest_error_valid_) { |
| *error_code = latest_error_reason_; |
| *stale_cache_entry_present = latest_error_stale_copy_in_cache_; |
| } |
| return latest_error_valid_; |
| } |
| |
| private: |
| bool latest_error_valid_; |
| int latest_error_reason_; |
| bool latest_error_stale_copy_in_cache_; |
| }; |
| |
| // Must be called on IO thread. |
| void InterceptNetworkTransactions(net::URLRequestContextGetter* getter, |
| net::Error error) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| net::HttpCache* cache( |
| getter->GetURLRequestContext()->http_transaction_factory()->GetCache()); |
| DCHECK(cache); |
| std::unique_ptr<net::FailingHttpTransactionFactory> factory( |
| new net::FailingHttpTransactionFactory(cache->GetSession(), error)); |
| // Throw away old version; since this is a browser test, there is no |
| // need to restore the old state. |
| cache->SetHttpNetworkTransactionFactoryForTesting(std::move(factory)); |
| } |
| |
| void CallOnUIThreadValidatingReturn(const base::Closure& callback, |
| int rv) { |
| DCHECK_EQ(net::OK, rv); |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, callback); |
| } |
| |
| // Must be called on IO thread. The callback will be called on |
| // completion of cache clearing on the UI thread. |
| void BackendClearCache(std::unique_ptr<disk_cache::Backend*> backend, |
| const base::Closure& callback, |
| int rv) { |
| DCHECK(*backend); |
| DCHECK_EQ(net::OK, rv); |
| (*backend)->DoomAllEntries( |
| base::Bind(&CallOnUIThreadValidatingReturn, callback)); |
| } |
| |
| // Must be called on IO thread. The callback will be called on |
| // completion of cache clearing on the UI thread. |
| void ClearCache(net::URLRequestContextGetter* getter, |
| const base::Closure& callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| net::HttpCache* cache( |
| getter->GetURLRequestContext()->http_transaction_factory()->GetCache()); |
| DCHECK(cache); |
| std::unique_ptr<disk_cache::Backend*> backend(new disk_cache::Backend*); |
| *backend = NULL; |
| disk_cache::Backend** backend_ptr = backend.get(); |
| |
| net::CompletionCallback backend_callback(base::Bind( |
| &BackendClearCache, base::Passed(std::move(backend)), callback)); |
| |
| // backend_ptr is valid until all copies of backend_callback go out |
| // of scope. |
| if (net::OK == cache->GetBackend(backend_ptr, backend_callback)) { |
| // The call completed synchronously, so GetBackend didn't run the callback. |
| backend_callback.Run(net::OK); |
| } |
| } |
| |
| } // namespace |
| |
| class RenderViewBrowserTest : public ContentBrowserTest { |
| public: |
| RenderViewBrowserTest() {} |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // This method is needed to allow interaction with in-process renderer |
| // and use of a test ContentRendererClient. |
| command_line->AppendSwitch(switches::kSingleProcess); |
| } |
| |
| void SetUpOnMainThread() override { |
| // Override setting of renderer client. |
| renderer_client_ = new TestShellContentRendererClient(); |
| // Explicitly leaks ownership; this object will remain alive |
| // until process death. We don't deleted the returned value, |
| // since some contexts set the pointer to a non-heap address. |
| SetRendererClientForTesting(renderer_client_); |
| } |
| |
| // Navigates to the given URL and waits for |num_navigations| to occur, and |
| // the title to change to |expected_title|. |
| void NavigateToURLAndWaitForTitle(const GURL& url, |
| const std::string& expected_title, |
| int num_navigations) { |
| content::TitleWatcher title_watcher( |
| shell()->web_contents(), base::ASCIIToUTF16(expected_title)); |
| |
| content::NavigateToURLBlockUntilNavigationsComplete( |
| shell(), url, num_navigations); |
| |
| EXPECT_EQ(base::ASCIIToUTF16(expected_title), |
| title_watcher.WaitAndGetTitle()); |
| } |
| |
| // Returns true if there is a valid error stored; in this case |
| // |*error_code| and |*stale_cache_entry_present| will be updated |
| // appropriately. |
| // Must be called after the renderer thread is created. |
| bool GetLatestErrorFromRendererClient( |
| int* error_code, bool* stale_cache_entry_present) { |
| bool result = false; |
| |
| PostTaskToInProcessRendererAndWait( |
| base::Bind(&RenderViewBrowserTest::GetLatestErrorFromRendererClient0, |
| renderer_client_, &result, error_code, |
| stale_cache_entry_present)); |
| return result; |
| } |
| |
| private: |
| // Must be run on renderer thread. |
| static void GetLatestErrorFromRendererClient0( |
| TestShellContentRendererClient* renderer_client, |
| bool* result, int* error_code, bool* stale_cache_entry_present) { |
| *result = renderer_client->GetLatestError( |
| error_code, stale_cache_entry_present); |
| } |
| |
| TestShellContentRendererClient* renderer_client_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(RenderViewBrowserTest, ConfirmCacheInformationPlumbed) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Load URL with "nocache" set, to create stale cache. |
| GURL test_url(embedded_test_server()->GetURL("/nocache.html")); |
| NavigateToURLAndWaitForTitle(test_url, "Nocache Test Page", 1); |
| |
| // Reload same URL after forcing an error from the the network layer; |
| // confirm that the error page is told the cached copy exists. |
| scoped_refptr<net::URLRequestContextGetter> url_request_context_getter = |
| shell()->web_contents()->GetRenderProcessHost()->GetStoragePartition()-> |
| GetURLRequestContext(); |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&InterceptNetworkTransactions, |
| base::RetainedRef(url_request_context_getter), |
| net::ERR_FAILED)); |
| |
| // An error results in one completed navigation. |
| NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1); |
| int error_code = net::OK; |
| bool stale_cache_entry_present = false; |
| ASSERT_TRUE(GetLatestErrorFromRendererClient( |
| &error_code, &stale_cache_entry_present)); |
| EXPECT_EQ(net::ERR_FAILED, error_code); |
| EXPECT_TRUE(stale_cache_entry_present); |
| |
| // Clear the cache and repeat; confirm lack of entry in cache reported. |
| scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner; |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&ClearCache, base::RetainedRef(url_request_context_getter), |
| runner->QuitClosure())); |
| runner->Run(); |
| |
| content::NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1); |
| |
| error_code = net::OK; |
| stale_cache_entry_present = true; |
| ASSERT_TRUE(GetLatestErrorFromRendererClient( |
| &error_code, &stale_cache_entry_present)); |
| EXPECT_EQ(net::ERR_FAILED, error_code); |
| EXPECT_FALSE(stale_cache_entry_present); |
| } |
| |
| } // namespace content |