1 // Copyright 2013 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 #include "chrome/browser/search/iframe_source.h" 6 7 #include "base/bind.h" 8 #include "base/memory/ref_counted_memory.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/message_loop/message_loop.h" 11 #include "chrome/browser/search/instant_io_context.h" 12 #include "content/public/browser/browser_thread.h" 13 #include "content/public/browser/resource_request_info.h" 14 #include "content/public/test/mock_resource_context.h" 15 #include "content/public/test/test_browser_thread_bundle.h" 16 #include "grit/browser_resources.h" 17 #include "net/base/request_priority.h" 18 #include "net/url_request/url_request.h" 19 #include "net/url_request/url_request_context.h" 20 #include "net/url_request/url_request_test_util.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 #include "url/gurl.h" 23 24 const int kNonInstantRendererPID = 0; 25 const char kNonInstantOrigin[] = "http://evil"; 26 const int kInstantRendererPID = 1; 27 const char kInstantOrigin[] = "chrome-search://instant"; 28 const int kInvalidRendererPID = 42; 29 30 class TestIframeSource : public IframeSource { 31 public: 32 using IframeSource::GetMimeType; 33 using IframeSource::ShouldServiceRequest; 34 using IframeSource::SendResource; 35 using IframeSource::SendJSWithOrigin; 36 37 protected: 38 virtual std::string GetSource() const OVERRIDE { 39 return "test"; 40 } 41 42 virtual bool ServesPath(const std::string& path) const OVERRIDE { 43 return path == "/valid.html" || path == "/valid.js"; 44 } 45 46 virtual void StartDataRequest( 47 const std::string& path, 48 int render_process_id, 49 int render_view_id, 50 const content::URLDataSource::GotDataCallback& callback) OVERRIDE { 51 } 52 53 // RenderViewHost is hard to mock in concert with everything else, so stub 54 // this method out for testing. 55 virtual bool GetOrigin( 56 int process_id, 57 int render_view_id, 58 std::string* origin) const OVERRIDE { 59 if (process_id == kInstantRendererPID) { 60 *origin = kInstantOrigin; 61 return true; 62 } 63 if (process_id == kNonInstantRendererPID) { 64 *origin = kNonInstantOrigin; 65 return true; 66 } 67 return false; 68 } 69 }; 70 71 class IframeSourceTest : public testing::Test { 72 public: 73 // net::URLRequest wants to be executed with a message loop that has TYPE_IO. 74 // InstantIOContext needs to be created on the UI thread and have everything 75 // else happen on the IO thread. This setup is a hacky way to satisfy all 76 // those constraints. 77 IframeSourceTest() 78 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), 79 resource_context_(&test_url_request_context_), 80 instant_io_context_(NULL), 81 response_(NULL) { 82 } 83 84 TestIframeSource* source() { return source_.get(); } 85 86 std::string response_string() { 87 if (response_.get()) { 88 return std::string(reinterpret_cast<const char*>(response_->front()), 89 response_->size()); 90 } 91 return ""; 92 } 93 94 net::URLRequest* MockRequest( 95 const std::string& url, 96 bool allocate_info, 97 int render_process_id, 98 int render_view_id) { 99 net::URLRequest* request = 100 new net::URLRequest(GURL(url), 101 net::DEFAULT_PRIORITY, 102 NULL, 103 resource_context_.GetRequestContext()); 104 if (allocate_info) { 105 content::ResourceRequestInfo::AllocateForTesting(request, 106 ResourceType::SUB_FRAME, 107 &resource_context_, 108 render_process_id, 109 render_view_id, 110 false); 111 } 112 return request; 113 } 114 115 void SendResource(int resource_id) { 116 source()->SendResource(resource_id, callback_); 117 } 118 119 void SendJSWithOrigin( 120 int resource_id, 121 int render_process_id, 122 int render_view_id) { 123 source()->SendJSWithOrigin(resource_id, render_process_id, render_view_id, 124 callback_); 125 } 126 127 private: 128 virtual void SetUp() OVERRIDE { 129 source_.reset(new TestIframeSource()); 130 callback_ = base::Bind(&IframeSourceTest::SaveResponse, 131 base::Unretained(this)); 132 instant_io_context_ = new InstantIOContext; 133 InstantIOContext::SetUserDataOnIO(&resource_context_, instant_io_context_); 134 InstantIOContext::AddInstantProcessOnIO(instant_io_context_, 135 kInstantRendererPID); 136 response_ = NULL; 137 } 138 139 virtual void TearDown() { 140 source_.reset(); 141 } 142 143 void SaveResponse(base::RefCountedMemory* data) { 144 response_ = data; 145 } 146 147 content::TestBrowserThreadBundle thread_bundle_; 148 149 net::TestURLRequestContext test_url_request_context_; 150 content::MockResourceContext resource_context_; 151 scoped_ptr<TestIframeSource> source_; 152 content::URLDataSource::GotDataCallback callback_; 153 scoped_refptr<InstantIOContext> instant_io_context_; 154 scoped_refptr<base::RefCountedMemory> response_; 155 }; 156 157 TEST_F(IframeSourceTest, ShouldServiceRequest) { 158 scoped_ptr<net::URLRequest> request; 159 request.reset(MockRequest("http://test/loader.js", true, 160 kNonInstantRendererPID, 0)); 161 EXPECT_FALSE(source()->ShouldServiceRequest(request.get())); 162 request.reset(MockRequest("chrome-search://bogus/valid.js", true, 163 kInstantRendererPID, 0)); 164 EXPECT_FALSE(source()->ShouldServiceRequest(request.get())); 165 request.reset(MockRequest("chrome-search://test/bogus.js", true, 166 kInstantRendererPID, 0)); 167 EXPECT_FALSE(source()->ShouldServiceRequest(request.get())); 168 request.reset(MockRequest("chrome-search://test/valid.js", true, 169 kInstantRendererPID, 0)); 170 EXPECT_TRUE(source()->ShouldServiceRequest(request.get())); 171 request.reset(MockRequest("chrome-search://test/valid.js", true, 172 kNonInstantRendererPID, 0)); 173 EXPECT_FALSE(source()->ShouldServiceRequest(request.get())); 174 request.reset(MockRequest("chrome-search://test/valid.js", true, 175 kInvalidRendererPID, 0)); 176 EXPECT_FALSE(source()->ShouldServiceRequest(request.get())); 177 } 178 179 TEST_F(IframeSourceTest, GetMimeType) { 180 // URLDataManagerBackend does not include / in path_and_query. 181 EXPECT_EQ("text/html", source()->GetMimeType("foo.html")); 182 EXPECT_EQ("application/javascript", source()->GetMimeType("foo.js")); 183 EXPECT_EQ("text/css", source()->GetMimeType("foo.css")); 184 EXPECT_EQ("image/png", source()->GetMimeType("foo.png")); 185 EXPECT_EQ("", source()->GetMimeType("bogus")); 186 } 187 188 TEST_F(IframeSourceTest, SendResource) { 189 SendResource(IDR_MOST_VISITED_TITLE_HTML); 190 EXPECT_FALSE(response_string().empty()); 191 } 192 193 TEST_F(IframeSourceTest, SendJSWithOrigin) { 194 SendJSWithOrigin(IDR_MOST_VISITED_TITLE_JS, kInstantRendererPID, 0); 195 EXPECT_FALSE(response_string().empty()); 196 SendJSWithOrigin(IDR_MOST_VISITED_TITLE_JS, kNonInstantRendererPID, 0); 197 EXPECT_FALSE(response_string().empty()); 198 SendJSWithOrigin(IDR_MOST_VISITED_TITLE_JS, kInvalidRendererPID, 0); 199 EXPECT_TRUE(response_string().empty()); 200 } 201