1 // Copyright (c) 2012 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.
5 #include "base/file_util.h"
6 #include "base/json/json_file_value_serializer.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/threading/thread.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_service_test_base.h"
12 #include "chrome/browser/extensions/unpacked_installer.h"
13 #include "chrome/browser/extensions/user_script_listener.h"
14 #include "chrome/common/chrome_paths.h"
15 #include "chrome/test/base/testing_profile.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/resource_controller.h"
18 #include "content/public/browser/resource_throttle.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "net/base/request_priority.h"
21 #include "net/url_request/url_request.h"
22 #include "net/url_request/url_request_filter.h"
23 #include "net/url_request/url_request_interceptor.h"
24 #include "net/url_request/url_request_test_job.h"
25 #include "net/url_request/url_request_test_util.h"
26 #include "testing/gtest/include/gtest/gtest.h"
28 using content::ResourceController;
29 using content::ResourceThrottle;
30 using content::ResourceType;
32 namespace extensions {
36 const char kMatchingUrl[] = "http://google.com/";
37 const char kNotMatchingUrl[] = "http://example.com/";
38 const char kTestData[] = "Hello, World!";
40 class ThrottleController : public base::SupportsUserData::Data,
41 public ResourceController {
43 ThrottleController(net::URLRequest* request, ResourceThrottle* throttle)
46 throttle_->set_controller_for_testing(this);
49 // ResourceController implementation:
50 virtual void Resume() OVERRIDE {
53 virtual void Cancel() OVERRIDE {
56 virtual void CancelAndIgnore() OVERRIDE {
59 virtual void CancelWithError(int error_code) OVERRIDE {
64 net::URLRequest* request_;
65 scoped_ptr<ResourceThrottle> throttle_;
68 // A simple test net::URLRequestJob. We don't care what it does, only that
69 // whether it starts and finishes.
70 class SimpleTestJob : public net::URLRequestTestJob {
72 SimpleTestJob(net::URLRequest* request,
73 net::NetworkDelegate* network_delegate)
74 : net::URLRequestTestJob(request,
80 virtual ~SimpleTestJob() {}
83 // Yoinked from extension_manifest_unittest.cc.
84 base::DictionaryValue* LoadManifestFile(const base::FilePath path,
86 EXPECT_TRUE(base::PathExists(path));
87 JSONFileValueSerializer serializer(path);
88 return static_cast<base::DictionaryValue*>(
89 serializer.Deserialize(NULL, error));
92 scoped_refptr<Extension> LoadExtension(const std::string& filename,
95 PathService::Get(chrome::DIR_TEST_DATA, &path);
97 AppendASCII("extensions").
98 AppendASCII("manifest_tests").
99 AppendASCII(filename.c_str());
100 scoped_ptr<base::DictionaryValue> value(LoadManifestFile(path, error));
103 return Extension::Create(path.DirName(), Manifest::UNPACKED, *value,
104 Extension::NO_FLAGS, error);
107 class SimpleTestJobURLRequestInterceptor
108 : public net::URLRequestInterceptor {
110 SimpleTestJobURLRequestInterceptor() {}
111 virtual ~SimpleTestJobURLRequestInterceptor() {}
113 // net::URLRequestJobFactory::ProtocolHandler
114 virtual net::URLRequestJob* MaybeInterceptRequest(
115 net::URLRequest* request,
116 net::NetworkDelegate* network_delegate) const OVERRIDE {
117 return new SimpleTestJob(request, network_delegate);
121 DISALLOW_COPY_AND_ASSIGN(SimpleTestJobURLRequestInterceptor);
126 class UserScriptListenerTest : public ExtensionServiceTestBase {
128 UserScriptListenerTest() {
129 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
130 "http", "google.com",
131 scoped_ptr<net::URLRequestInterceptor>(
132 new SimpleTestJobURLRequestInterceptor()));
133 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
134 "http", "example.com",
135 scoped_ptr<net::URLRequestInterceptor>(
136 new SimpleTestJobURLRequestInterceptor()));
139 virtual ~UserScriptListenerTest() {
140 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler("http",
142 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler("http",
146 virtual void SetUp() OVERRIDE {
147 ExtensionServiceTestBase::SetUp();
149 InitializeEmptyExtensionService();
151 base::MessageLoop::current()->RunUntilIdle();
153 listener_ = new UserScriptListener();
156 virtual void TearDown() OVERRIDE {
158 base::MessageLoop::current()->RunUntilIdle();
159 ExtensionServiceTestBase::TearDown();
163 net::TestURLRequest* StartTestRequest(net::URLRequest::Delegate* delegate,
164 const std::string& url_string,
165 net::TestURLRequestContext* context) {
166 GURL url(url_string);
167 net::TestURLRequest* request =
168 new net::TestURLRequest(url, net::DEFAULT_PRIORITY, delegate, context);
170 ResourceThrottle* throttle = listener_->CreateResourceThrottle(
171 url, content::RESOURCE_TYPE_MAIN_FRAME);
175 request->SetUserData(NULL, new ThrottleController(request, throttle));
177 throttle->WillStartRequest(&defer);
186 void LoadTestExtension() {
187 base::FilePath test_dir;
188 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
189 base::FilePath extension_path = test_dir
190 .AppendASCII("extensions")
192 .AppendASCII("Extensions")
193 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
194 .AppendASCII("1.0.0.0");
195 UnpackedInstaller::Create(service_)->Load(extension_path);
198 void UnloadTestExtension() {
199 ASSERT_FALSE(service_->extensions()->is_empty());
200 service_->UnloadExtension((*service_->extensions()->begin())->id(),
201 UnloadedExtensionInfo::REASON_DISABLE);
204 scoped_refptr<UserScriptListener> listener_;
209 TEST_F(UserScriptListenerTest, DelayAndUpdate) {
211 base::MessageLoop::current()->RunUntilIdle();
213 net::TestDelegate delegate;
214 net::TestURLRequestContext context;
215 scoped_ptr<net::TestURLRequest> request(
216 StartTestRequest(&delegate, kMatchingUrl, &context));
217 ASSERT_FALSE(request->is_pending());
219 content::NotificationService::current()->Notify(
220 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
221 content::Source<Profile>(profile_.get()),
222 content::NotificationService::NoDetails());
223 base::MessageLoop::current()->RunUntilIdle();
224 EXPECT_EQ(kTestData, delegate.data_received());
227 TEST_F(UserScriptListenerTest, DelayAndUnload) {
229 base::MessageLoop::current()->RunUntilIdle();
231 net::TestDelegate delegate;
232 net::TestURLRequestContext context;
233 scoped_ptr<net::TestURLRequest> request(
234 StartTestRequest(&delegate, kMatchingUrl, &context));
235 ASSERT_FALSE(request->is_pending());
237 UnloadTestExtension();
238 base::MessageLoop::current()->RunUntilIdle();
240 // This is still not enough to start delayed requests. We have to notify the
241 // listener that the user scripts have been updated.
242 ASSERT_FALSE(request->is_pending());
244 content::NotificationService::current()->Notify(
245 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
246 content::Source<Profile>(profile_.get()),
247 content::NotificationService::NoDetails());
248 base::MessageLoop::current()->RunUntilIdle();
249 EXPECT_EQ(kTestData, delegate.data_received());
252 TEST_F(UserScriptListenerTest, NoDelayNoExtension) {
253 net::TestDelegate delegate;
254 net::TestURLRequestContext context;
255 scoped_ptr<net::TestURLRequest> request(
256 StartTestRequest(&delegate, kMatchingUrl, &context));
258 // The request should be started immediately.
259 ASSERT_TRUE(request->is_pending());
261 base::MessageLoop::current()->RunUntilIdle();
262 EXPECT_EQ(kTestData, delegate.data_received());
265 TEST_F(UserScriptListenerTest, NoDelayNotMatching) {
267 base::MessageLoop::current()->RunUntilIdle();
269 net::TestDelegate delegate;
270 net::TestURLRequestContext context;
271 scoped_ptr<net::TestURLRequest> request(StartTestRequest(&delegate,
275 // The request should be started immediately.
276 ASSERT_TRUE(request->is_pending());
278 base::MessageLoop::current()->RunUntilIdle();
279 EXPECT_EQ(kTestData, delegate.data_received());
282 TEST_F(UserScriptListenerTest, MultiProfile) {
284 base::MessageLoop::current()->RunUntilIdle();
286 // Fire up a second profile and have it load an extension with a content
288 TestingProfile profile2;
290 scoped_refptr<Extension> extension = LoadExtension(
291 "content_script_yahoo.json", &error);
292 ASSERT_TRUE(extension.get());
294 extensions::ExtensionRegistry::Get(&profile2)->AddEnabled(extension);
296 content::NotificationService::current()->Notify(
297 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
298 content::Source<Profile>(&profile2),
299 content::Details<Extension>(extension.get()));
301 net::TestDelegate delegate;
302 net::TestURLRequestContext context;
303 scoped_ptr<net::TestURLRequest> request(
304 StartTestRequest(&delegate, kMatchingUrl, &context));
305 ASSERT_FALSE(request->is_pending());
307 // When the first profile's user scripts are ready, the request should still
308 // be blocked waiting for profile2.
309 content::NotificationService::current()->Notify(
310 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
311 content::Source<Profile>(profile_.get()),
312 content::NotificationService::NoDetails());
313 base::MessageLoop::current()->RunUntilIdle();
314 ASSERT_FALSE(request->is_pending());
315 EXPECT_TRUE(delegate.data_received().empty());
317 // After profile2 is ready, the request should proceed.
318 content::NotificationService::current()->Notify(
319 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
320 content::Source<Profile>(&profile2),
321 content::NotificationService::NoDetails());
322 base::MessageLoop::current()->RunUntilIdle();
323 EXPECT_EQ(kTestData, delegate.data_received());
326 // Test when the script updated notification occurs before the throttle's
327 // WillStartRequest function is called. This can occur when there are multiple
329 TEST_F(UserScriptListenerTest, ResumeBeforeStart) {
331 base::MessageLoop::current()->RunUntilIdle();
332 net::TestDelegate delegate;
333 net::TestURLRequestContext context;
334 GURL url(kMatchingUrl);
335 scoped_ptr<net::TestURLRequest> request(
336 new net::TestURLRequest(url, net::DEFAULT_PRIORITY, &delegate, &context));
338 ResourceThrottle* throttle =
339 listener_->CreateResourceThrottle(url, content::RESOURCE_TYPE_MAIN_FRAME);
340 ASSERT_TRUE(throttle);
341 request->SetUserData(NULL, new ThrottleController(request.get(), throttle));
343 ASSERT_FALSE(request->is_pending());
345 content::NotificationService::current()->Notify(
346 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
347 content::Source<Profile>(profile_.get()),
348 content::NotificationService::NoDetails());
349 base::MessageLoop::current()->RunUntilIdle();
352 throttle->WillStartRequest(&defer);
358 } // namespace extensions