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_unittest.h"
11 #include "chrome/browser/extensions/unpacked_installer.h"
12 #include "chrome/browser/extensions/user_script_listener.h"
13 #include "chrome/common/chrome_paths.h"
14 #include "chrome/test/base/testing_profile.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/resource_controller.h"
17 #include "content/public/browser/resource_throttle.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "net/base/request_priority.h"
20 #include "net/url_request/url_request.h"
21 #include "net/url_request/url_request_filter.h"
22 #include "net/url_request/url_request_test_job.h"
23 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 using content::ResourceController;
27 using content::ResourceThrottle;
29 namespace extensions {
33 const char kMatchingUrl[] = "http://google.com/";
34 const char kNotMatchingUrl[] = "http://example.com/";
35 const char kTestData[] = "Hello, World!";
37 class ThrottleController : public base::SupportsUserData::Data,
38 public ResourceController {
40 ThrottleController(net::URLRequest* request, ResourceThrottle* throttle)
43 throttle_->set_controller_for_testing(this);
46 // ResourceController implementation:
47 virtual void Resume() OVERRIDE {
50 virtual void Cancel() OVERRIDE {
53 virtual void CancelAndIgnore() OVERRIDE {
56 virtual void CancelWithError(int error_code) OVERRIDE {
61 net::URLRequest* request_;
62 scoped_ptr<ResourceThrottle> throttle_;
65 // A simple test net::URLRequestJob. We don't care what it does, only that
66 // whether it starts and finishes.
67 class SimpleTestJob : public net::URLRequestTestJob {
69 SimpleTestJob(net::URLRequest* request,
70 net::NetworkDelegate* network_delegate)
71 : net::URLRequestTestJob(request,
77 virtual ~SimpleTestJob() {}
80 // Yoinked from extension_manifest_unittest.cc.
81 base::DictionaryValue* LoadManifestFile(const base::FilePath path,
83 EXPECT_TRUE(base::PathExists(path));
84 JSONFileValueSerializer serializer(path);
85 return static_cast<base::DictionaryValue*>(
86 serializer.Deserialize(NULL, error));
89 scoped_refptr<Extension> LoadExtension(const std::string& filename,
92 PathService::Get(chrome::DIR_TEST_DATA, &path);
94 AppendASCII("extensions").
95 AppendASCII("manifest_tests").
96 AppendASCII(filename.c_str());
97 scoped_ptr<base::DictionaryValue> value(LoadManifestFile(path, error));
100 return Extension::Create(path.DirName(), Manifest::UNPACKED, *value,
101 Extension::NO_FLAGS, error);
104 class SimpleTestJobProtocolHandler
105 : public net::URLRequestJobFactory::ProtocolHandler {
107 SimpleTestJobProtocolHandler() {}
108 virtual ~SimpleTestJobProtocolHandler() {}
110 // net::URLRequestJobFactory::ProtocolHandler
111 virtual net::URLRequestJob* MaybeCreateJob(
112 net::URLRequest* request,
113 net::NetworkDelegate* network_delegate) const OVERRIDE {
114 return new SimpleTestJob(request, network_delegate);
118 DISALLOW_COPY_AND_ASSIGN(SimpleTestJobProtocolHandler);
123 class UserScriptListenerTest : public ExtensionServiceTestBase {
125 UserScriptListenerTest() {
126 net::URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
127 "http", "google.com",
128 scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(
129 new SimpleTestJobProtocolHandler()));
130 net::URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
131 "http", "example.com",
132 scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(
133 new SimpleTestJobProtocolHandler()));
136 virtual ~UserScriptListenerTest() {
137 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler("http",
139 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler("http",
143 virtual void SetUp() OVERRIDE {
144 ExtensionServiceTestBase::SetUp();
146 InitializeEmptyExtensionService();
148 base::MessageLoop::current()->RunUntilIdle();
150 listener_ = new UserScriptListener();
153 virtual void TearDown() OVERRIDE {
155 base::MessageLoop::current()->RunUntilIdle();
156 ExtensionServiceTestBase::TearDown();
160 net::TestURLRequest* StartTestRequest(net::URLRequest::Delegate* delegate,
161 const std::string& url_string,
162 net::TestURLRequestContext* context) {
163 GURL url(url_string);
164 net::TestURLRequest* request =
165 new net::TestURLRequest(url, net::DEFAULT_PRIORITY, delegate, context);
167 ResourceThrottle* throttle =
168 listener_->CreateResourceThrottle(url, ResourceType::MAIN_FRAME);
172 request->SetUserData(NULL, new ThrottleController(request, throttle));
174 throttle->WillStartRequest(&defer);
183 void LoadTestExtension() {
184 base::FilePath test_dir;
185 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
186 base::FilePath extension_path = test_dir
187 .AppendASCII("extensions")
189 .AppendASCII("Extensions")
190 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
191 .AppendASCII("1.0.0.0");
192 UnpackedInstaller::Create(service_)->Load(extension_path);
195 void UnloadTestExtension() {
196 ASSERT_FALSE(service_->extensions()->is_empty());
197 service_->UnloadExtension((*service_->extensions()->begin())->id(),
198 UnloadedExtensionInfo::REASON_DISABLE);
201 scoped_refptr<UserScriptListener> listener_;
206 TEST_F(UserScriptListenerTest, DelayAndUpdate) {
208 base::MessageLoop::current()->RunUntilIdle();
210 net::TestDelegate delegate;
211 net::TestURLRequestContext context;
212 scoped_ptr<net::TestURLRequest> request(
213 StartTestRequest(&delegate, kMatchingUrl, &context));
214 ASSERT_FALSE(request->is_pending());
216 content::NotificationService::current()->Notify(
217 chrome::NOTIFICATION_USER_SCRIPTS_UPDATED,
218 content::Source<Profile>(profile_.get()),
219 content::NotificationService::NoDetails());
220 base::MessageLoop::current()->RunUntilIdle();
221 EXPECT_EQ(kTestData, delegate.data_received());
224 TEST_F(UserScriptListenerTest, DelayAndUnload) {
226 base::MessageLoop::current()->RunUntilIdle();
228 net::TestDelegate delegate;
229 net::TestURLRequestContext context;
230 scoped_ptr<net::TestURLRequest> request(
231 StartTestRequest(&delegate, kMatchingUrl, &context));
232 ASSERT_FALSE(request->is_pending());
234 UnloadTestExtension();
235 base::MessageLoop::current()->RunUntilIdle();
237 // This is still not enough to start delayed requests. We have to notify the
238 // listener that the user scripts have been updated.
239 ASSERT_FALSE(request->is_pending());
241 content::NotificationService::current()->Notify(
242 chrome::NOTIFICATION_USER_SCRIPTS_UPDATED,
243 content::Source<Profile>(profile_.get()),
244 content::NotificationService::NoDetails());
245 base::MessageLoop::current()->RunUntilIdle();
246 EXPECT_EQ(kTestData, delegate.data_received());
249 TEST_F(UserScriptListenerTest, NoDelayNoExtension) {
250 net::TestDelegate delegate;
251 net::TestURLRequestContext context;
252 scoped_ptr<net::TestURLRequest> request(
253 StartTestRequest(&delegate, kMatchingUrl, &context));
255 // The request should be started immediately.
256 ASSERT_TRUE(request->is_pending());
258 base::MessageLoop::current()->RunUntilIdle();
259 EXPECT_EQ(kTestData, delegate.data_received());
262 TEST_F(UserScriptListenerTest, NoDelayNotMatching) {
264 base::MessageLoop::current()->RunUntilIdle();
266 net::TestDelegate delegate;
267 net::TestURLRequestContext context;
268 scoped_ptr<net::TestURLRequest> request(StartTestRequest(&delegate,
272 // The request should be started immediately.
273 ASSERT_TRUE(request->is_pending());
275 base::MessageLoop::current()->RunUntilIdle();
276 EXPECT_EQ(kTestData, delegate.data_received());
279 TEST_F(UserScriptListenerTest, MultiProfile) {
281 base::MessageLoop::current()->RunUntilIdle();
283 // Fire up a second profile and have it load an extension with a content
285 TestingProfile profile2;
287 scoped_refptr<Extension> extension = LoadExtension(
288 "content_script_yahoo.json", &error);
289 ASSERT_TRUE(extension.get());
291 extensions::ExtensionRegistry::Get(&profile2)->AddEnabled(extension);
293 content::NotificationService::current()->Notify(
294 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
295 content::Source<Profile>(&profile2),
296 content::Details<Extension>(extension.get()));
298 net::TestDelegate delegate;
299 net::TestURLRequestContext context;
300 scoped_ptr<net::TestURLRequest> request(
301 StartTestRequest(&delegate, kMatchingUrl, &context));
302 ASSERT_FALSE(request->is_pending());
304 // When the first profile's user scripts are ready, the request should still
305 // be blocked waiting for profile2.
306 content::NotificationService::current()->Notify(
307 chrome::NOTIFICATION_USER_SCRIPTS_UPDATED,
308 content::Source<Profile>(profile_.get()),
309 content::NotificationService::NoDetails());
310 base::MessageLoop::current()->RunUntilIdle();
311 ASSERT_FALSE(request->is_pending());
312 EXPECT_TRUE(delegate.data_received().empty());
314 // After profile2 is ready, the request should proceed.
315 content::NotificationService::current()->Notify(
316 chrome::NOTIFICATION_USER_SCRIPTS_UPDATED,
317 content::Source<Profile>(&profile2),
318 content::NotificationService::NoDetails());
319 base::MessageLoop::current()->RunUntilIdle();
320 EXPECT_EQ(kTestData, delegate.data_received());
323 // Test when the script updated notification occurs before the throttle's
324 // WillStartRequest function is called. This can occur when there are multiple
326 TEST_F(UserScriptListenerTest, ResumeBeforeStart) {
328 base::MessageLoop::current()->RunUntilIdle();
329 net::TestDelegate delegate;
330 net::TestURLRequestContext context;
331 GURL url(kMatchingUrl);
332 scoped_ptr<net::TestURLRequest> request(
333 new net::TestURLRequest(url, net::DEFAULT_PRIORITY, &delegate, &context));
335 ResourceThrottle* throttle =
336 listener_->CreateResourceThrottle(url, ResourceType::MAIN_FRAME);
337 ASSERT_TRUE(throttle);
338 request->SetUserData(NULL, new ThrottleController(request.get(), throttle));
340 ASSERT_FALSE(request->is_pending());
342 content::NotificationService::current()->Notify(
343 chrome::NOTIFICATION_USER_SCRIPTS_UPDATED,
344 content::Source<Profile>(profile_.get()),
345 content::NotificationService::NoDetails());
346 base::MessageLoop::current()->RunUntilIdle();
349 throttle->WillStartRequest(&defer);
355 } // namespace extensions