Index: chrome/browser/errorpage_browsertest.cc |
diff --git a/chrome/browser/errorpage_browsertest.cc b/chrome/browser/errorpage_browsertest.cc |
index e2afcbf59b7c601ef1b81187426d68a6d2e86b55..7a7b66748ec9edf29a9965c148be8accb81a7b15 100644 |
--- a/chrome/browser/errorpage_browsertest.cc |
+++ b/chrome/browser/errorpage_browsertest.cc |
@@ -6,6 +6,8 @@ |
#include "base/prefs/pref_service.h" |
#include "base/strings/stringprintf.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "chrome/browser/browsing_data/browsing_data_helper.h" |
+#include "chrome/browser/browsing_data/browsing_data_remover.h" |
#include "chrome/browser/google/google_util.h" |
#include "chrome/browser/net/url_request_mock_util.h" |
#include "chrome/browser/profiles/profile.h" |
@@ -25,6 +27,12 @@ |
#include "content/test/net/url_request_mock_http_job.h" |
#include "net/base/net_errors.h" |
#include "net/base/net_util.h" |
+#include "net/http/http_cache.h" |
+#include "net/http/http_transaction.h" |
+#include "net/http/http_transaction_factory.h" |
+#include "net/test/spawned_test_server/spawned_test_server.h" |
+#include "net/url_request/url_request_context.h" |
+#include "net/url_request/url_request_context_getter.h" |
#include "net/url_request/url_request_filter.h" |
#include "net/url_request/url_request_job_factory.h" |
@@ -121,7 +129,6 @@ class ErrorPageTest : public InProcessBrowserTest { |
} |
}; |
- |
class TestFailProvisionalLoadObserver : public content::WebContentsObserver { |
public: |
explicit TestFailProvisionalLoadObserver(content::WebContents* contents) |
@@ -148,6 +155,143 @@ class TestFailProvisionalLoadObserver : public content::WebContentsObserver { |
DISALLOW_COPY_AND_ASSIGN(TestFailProvisionalLoadObserver); |
}; |
+class TestNetworkTransaction : public net::HttpTransaction { |
+ public: |
+ explicit TestNetworkTransaction(enum net::Error err): err_(err) { |
+ DCHECK_NE(net::OK, err); |
+ } |
+ virtual ~TestNetworkTransaction() {} |
+ |
+ // net::HttpTransaction |
+ virtual int Start(const net::HttpRequestInfo* request_info, |
+ const net::CompletionCallback& callback, |
+ const net::BoundNetLog& net_log) OVERRIDE { |
+ base::MessageLoop::current()->PostTask( |
+ FROM_HERE, base::Bind(callback, err_)); |
+ return net::ERR_IO_PENDING; |
+ } |
+ virtual int RestartIgnoringLastError( |
+ const net::CompletionCallback& callback) OVERRIDE { |
+ return net::ERR_FAILED; |
+ } |
+ virtual int RestartWithCertificate( |
+ net::X509Certificate* client_cert, |
+ const net::CompletionCallback& callback) OVERRIDE { |
+ return net::ERR_FAILED; |
+ } |
+ virtual int RestartWithAuth( |
+ const net::AuthCredentials& credentials, |
+ const net::CompletionCallback& callback) OVERRIDE { |
+ return net::ERR_FAILED; |
+ } |
+ virtual bool IsReadyToRestartForAuth() OVERRIDE { |
+ return false; |
+ } |
+ virtual int Read(net::IOBuffer* buf, int buf_len, |
+ const net::CompletionCallback& callback) OVERRIDE { |
+ NOTREACHED(); |
+ return net::ERR_FAILED; |
+ } |
+ virtual void StopCaching() OVERRIDE {} |
+ virtual bool GetFullRequestHeaders( |
+ net::HttpRequestHeaders* headers) const OVERRIDE { |
+ return false; |
+ } |
+ virtual int64 GetTotalReceivedBytes() const OVERRIDE { |
+ return 0; |
+ } |
+ virtual void DoneReading() OVERRIDE { |
+ NOTREACHED(); |
+ } |
+ virtual const net::HttpResponseInfo* GetResponseInfo() const OVERRIDE { |
+ return NULL; |
+ } |
+ virtual net::LoadState GetLoadState() const OVERRIDE { |
+ return net::LOAD_STATE_IDLE; |
+ } |
+ virtual net::UploadProgress GetUploadProgress() const OVERRIDE { |
+ return net::UploadProgress(); |
+ } |
+ virtual bool GetLoadTimingInfo( |
+ net::LoadTimingInfo* load_timing_info) const OVERRIDE { |
+ // Shouldn't be relevant; using the minimal set from |
+ // http_transaction_unittest.cc MockNetworkTransaction::GetLoadTimingInfo(). |
+ load_timing_info->socket_reused = true; |
+ load_timing_info->send_start = base::TimeTicks::Now(); |
+ load_timing_info->send_end = base::TimeTicks::Now(); |
+ return true; |
+ } |
+ virtual void SetPriority(net::RequestPriority priority) OVERRIDE {} |
+ virtual void SetWebSocketHandshakeStreamCreateHelper( |
+ net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) OVERRIDE { |
+ NOTREACHED(); |
+ } |
+ virtual void SetBeforeNetworkStartCallback( |
+ const BeforeNetworkStartCallback& callback) OVERRIDE { |
+ } |
+ virtual int ResumeNetworkStart() OVERRIDE { |
+ NOTREACHED(); |
+ return net::ERR_FAILED; |
+ } |
+ |
+ private: |
+ enum net::Error err_; |
+}; |
+ |
+// Creates transactions that always (asynchronously) return |err|. |
+// |err| must not be net::OK. |
+class TestNetworkTransactionFactory : public net::HttpTransactionFactory { |
+ public: |
+ explicit TestNetworkTransactionFactory(enum net::Error err) : err_(err) {} |
+ virtual ~TestNetworkTransactionFactory() {} |
+ |
+ // net::HttpTransactionFactory: |
+ virtual int CreateTransaction( |
+ net::RequestPriority priority, |
+ scoped_ptr<net::HttpTransaction>* trans) OVERRIDE { |
+ trans->reset(new TestNetworkTransaction(err_)); |
+ return net::OK; |
+ } |
+ virtual net::HttpCache* GetCache() OVERRIDE { |
+ return NULL; |
+ } |
+ virtual net::HttpNetworkSession* GetSession() OVERRIDE { |
+ return NULL; // TODO(rdsmith): Is this really safe? |
+ } |
+ |
+ static void SetupInstance(net::URLRequestContextGetter* getter, |
+ enum net::Error err) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ net::HttpCache* cache( |
+ getter->GetURLRequestContext()->http_transaction_factory()->GetCache()); |
+ DCHECK(cache); |
+ DCHECK(!old_network_layer_.get()); |
+ scoped_ptr<TestNetworkTransactionFactory> factory( |
+ new TestNetworkTransactionFactory(err)); |
+ old_network_layer_ = cache->SetNetworkLayerForTesting( |
+ factory.PassAs<net::HttpTransactionFactory>()); |
+ } |
+ |
+ static void TearDownInstance(net::URLRequestContextGetter* getter) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ net::HttpCache* cache( |
+ getter->GetURLRequestContext()->http_transaction_factory()->GetCache()); |
+ DCHECK(cache); |
+ DCHECK(old_network_layer_.get()); |
+ cache->SetNetworkLayerForTesting(old_network_layer_.Pass()); |
+ } |
+ |
+ private: |
+ enum net::Error err_; |
+ |
+ // It is the caller's responsibility to make sure that TearDownInstance() |
+ // is called so that this does not leak. |
+ static scoped_ptr<net::HttpTransactionFactory> old_network_layer_; |
+}; |
+ |
+scoped_ptr<net::HttpTransactionFactory> |
+TestNetworkTransactionFactory::old_network_layer_; |
+ |
// See crbug.com/109669 |
#if defined(USE_AURA) || defined(OS_WIN) |
#define MAYBE_DNSError_Basic DISABLED_DNSError_Basic |
@@ -356,6 +500,76 @@ IN_PROC_BROWSER_TEST_F(ErrorPageTest, Page404) { |
1); |
} |
+// Checks that when an error occurs, the stale cache status of the page |
+// is correctly transferred. |
+IN_PROC_BROWSER_TEST_F(ErrorPageTest, StaleCacheStatus) { |
+ ASSERT_TRUE(test_server()->Start()); |
+ // Load cache with entry with "nocache" set, to create stale |
+ // cache. |
+ GURL test_url(test_server()->GetURL("files/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 = |
+ browser()->profile()->GetRequestContext(); |
+ BrowserThread::PostTask( |
+ BrowserThread::IO, FROM_HERE, |
+ base::Bind(&TestNetworkTransactionFactory::SetupInstance, |
+ url_request_context_getter, |
+ // Note that we can't use an error that'll invoke the link |
+ // doctor. In normal network error conditions that would |
+ // work (because the link doctor fetch would also fail, |
+ // putting us back in the main offline path), but |
+ // SetUrlRequestMocksEnabled() has also specfied a link |
+ // doctor mock, which will be accessible because it |
+ // won't go through the network cache. |
+ net::ERR_FAILED)); |
+ |
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( |
+ // First fails, second to error page. |
+ // TODO(rdsmith): The above is wrong; why? |
+ browser(), test_url, 1 /* 2 */); |
+ |
+ content::WebContents* wc = |
+ browser()->tab_strip_model()->GetActiveWebContents(); |
+ const char* js_cache_probe = |
+ "(function () {\n" |
+ "if ('staleCopyInCache' in templateData) {\n" |
+ "return templateData.staleCopyInCache;\n" |
+ "} else {\n" |
+ "return 'Undefined';\n" |
+ "}\n" |
+ "})();"; |
+ |
+ scoped_ptr<base::Value> value = |
+ content::ExecuteScriptAndGetValue( |
+ wc->GetRenderViewHost(), js_cache_probe); |
+ ASSERT_TRUE(value->IsType(base::Value::TYPE_BOOLEAN)) << "Type is " |
+ << value->GetType(); |
+ bool result = false; |
+ EXPECT_TRUE(value->GetAsBoolean(&result)); |
+ EXPECT_TRUE(result); |
+ |
+ // Clear the cache and reload the same URL; confirm the error page is told |
+ // that there is no cached copy. |
+ BrowsingDataRemover* remover = |
+ BrowsingDataRemover::CreateForUnboundedRange(browser()->profile()); |
+ remover->Remove(BrowsingDataRemover::REMOVE_CACHE, |
+ BrowsingDataHelper::UNPROTECTED_WEB); |
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( |
+ browser(), test_url, 1); // First fails, second to error page. |
+ value = content::ExecuteScriptAndGetValue( |
+ wc->GetRenderViewHost(), js_cache_probe); |
+ ASSERT_TRUE(value->IsType(base::Value::TYPE_BOOLEAN)); |
+ EXPECT_TRUE(value->GetAsBoolean(&result)); |
+ EXPECT_FALSE(result); |
+ BrowserThread::PostTask( |
+ BrowserThread::IO, FROM_HERE, |
+ base::Bind(&TestNetworkTransactionFactory::TearDownInstance, |
+ url_request_context_getter)); |
+} |
+ |
// Returns Javascript code that executes plain text search for the page. |
// Pass into content::ExecuteScriptAndExtractBool as |script| parameter. |
std::string GetTextContentContainsStringScript( |