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/files/file_util.h"
6 #include "base/json/json_file_value_serializer.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/threading/thread.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_service_test_base.h"
13 #include "chrome/browser/extensions/unpacked_installer.h"
14 #include "chrome/browser/extensions/user_script_listener.h"
15 #include "chrome/common/chrome_paths.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/resource_controller.h"
19 #include "content/public/browser/resource_throttle.h"
20 #include "extensions/browser/extension_registry.h"
21 #include "net/base/request_priority.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_filter.h"
24 #include "net/url_request/url_request_interceptor.h"
25 #include "net/url_request/url_request_test_job.h"
26 #include "net/url_request/url_request_test_util.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 using content::ResourceController;
30 using content::ResourceThrottle;
31 using content::ResourceType;
33 namespace extensions {
37 const char kMatchingUrl[] = "http://google.com/";
38 const char kNotMatchingUrl[] = "http://example.com/";
39 const char kTestData[] = "Hello, World!";
41 class ThrottleController : public base::SupportsUserData::Data,
42 public ResourceController {
44 ThrottleController(net::URLRequest* request, ResourceThrottle* throttle)
47 throttle_->set_controller_for_testing(this);
50 // ResourceController implementation:
51 void Resume() override { request_->Start(); }
52 void Cancel() override { NOTREACHED(); }
53 void CancelAndIgnore() override { NOTREACHED(); }
54 void CancelWithError(int error_code) override { NOTREACHED(); }
57 net::URLRequest* request_;
58 scoped_ptr<ResourceThrottle> throttle_;
61 // A simple test net::URLRequestJob. We don't care what it does, only that
62 // whether it starts and finishes.
63 class SimpleTestJob : public net::URLRequestTestJob {
65 SimpleTestJob(net::URLRequest* request,
66 net::NetworkDelegate* network_delegate)
67 : net::URLRequestTestJob(request,
73 ~SimpleTestJob() override {}
76 // Yoinked from extension_manifest_unittest.cc.
77 base::DictionaryValue* LoadManifestFile(const base::FilePath path,
79 EXPECT_TRUE(base::PathExists(path));
80 JSONFileValueSerializer serializer(path);
81 return static_cast<base::DictionaryValue*>(
82 serializer.Deserialize(NULL, error));
85 scoped_refptr<Extension> LoadExtension(const std::string& filename,
88 PathService::Get(chrome::DIR_TEST_DATA, &path);
90 AppendASCII("extensions").
91 AppendASCII("manifest_tests").
92 AppendASCII(filename.c_str());
93 scoped_ptr<base::DictionaryValue> value(LoadManifestFile(path, error));
96 return Extension::Create(path.DirName(), Manifest::UNPACKED, *value,
97 Extension::NO_FLAGS, error);
100 class SimpleTestJobURLRequestInterceptor
101 : public net::URLRequestInterceptor {
103 SimpleTestJobURLRequestInterceptor() {}
104 ~SimpleTestJobURLRequestInterceptor() override {}
106 // net::URLRequestJobFactory::ProtocolHandler
107 net::URLRequestJob* MaybeInterceptRequest(
108 net::URLRequest* request,
109 net::NetworkDelegate* network_delegate) const override {
110 return new SimpleTestJob(request, network_delegate);
114 DISALLOW_COPY_AND_ASSIGN(SimpleTestJobURLRequestInterceptor);
119 class UserScriptListenerTest : public ExtensionServiceTestBase {
121 UserScriptListenerTest() {
122 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
123 "http", "google.com",
124 scoped_ptr<net::URLRequestInterceptor>(
125 new SimpleTestJobURLRequestInterceptor()));
126 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
127 "http", "example.com",
128 scoped_ptr<net::URLRequestInterceptor>(
129 new SimpleTestJobURLRequestInterceptor()));
132 ~UserScriptListenerTest() override {
133 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler("http",
135 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler("http",
139 void SetUp() override {
140 ExtensionServiceTestBase::SetUp();
142 InitializeEmptyExtensionService();
144 base::MessageLoop::current()->RunUntilIdle();
146 listener_ = new UserScriptListener();
149 void TearDown() override {
151 base::MessageLoop::current()->RunUntilIdle();
152 ExtensionServiceTestBase::TearDown();
156 scoped_ptr<net::URLRequest> StartTestRequest(
157 net::URLRequest::Delegate* delegate,
158 const std::string& url_string,
159 net::TestURLRequestContext* context) {
160 GURL url(url_string);
161 scoped_ptr<net::URLRequest> request(context->CreateRequest(
162 url, net::DEFAULT_PRIORITY, delegate, NULL));
164 ResourceThrottle* throttle = listener_->CreateResourceThrottle(
165 url, content::RESOURCE_TYPE_MAIN_FRAME);
169 request->SetUserData(NULL,
170 new ThrottleController(request.get(), throttle));
172 throttle->WillStartRequest(&defer);
178 return request.Pass();
181 void LoadTestExtension() {
182 base::FilePath test_dir;
183 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
184 base::FilePath extension_path = test_dir
185 .AppendASCII("extensions")
187 .AppendASCII("Extensions")
188 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
189 .AppendASCII("1.0.0.0");
190 UnpackedInstaller::Create(service_)->Load(extension_path);
193 void UnloadTestExtension() {
194 ASSERT_FALSE(service_->extensions()->is_empty());
195 service_->UnloadExtension((*service_->extensions()->begin())->id(),
196 UnloadedExtensionInfo::REASON_DISABLE);
199 scoped_refptr<UserScriptListener> listener_;
204 TEST_F(UserScriptListenerTest, DelayAndUpdate) {
206 base::MessageLoop::current()->RunUntilIdle();
208 net::TestDelegate delegate;
209 net::TestURLRequestContext context;
210 scoped_ptr<net::URLRequest> request(
211 StartTestRequest(&delegate, kMatchingUrl, &context));
212 ASSERT_FALSE(request->is_pending());
214 content::NotificationService::current()->Notify(
215 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
216 content::Source<Profile>(profile_.get()),
217 content::NotificationService::NoDetails());
218 base::MessageLoop::current()->RunUntilIdle();
219 EXPECT_EQ(kTestData, delegate.data_received());
222 TEST_F(UserScriptListenerTest, DelayAndUnload) {
224 base::MessageLoop::current()->RunUntilIdle();
226 net::TestDelegate delegate;
227 net::TestURLRequestContext context;
228 scoped_ptr<net::URLRequest> request(
229 StartTestRequest(&delegate, kMatchingUrl, &context));
230 ASSERT_FALSE(request->is_pending());
232 UnloadTestExtension();
233 base::MessageLoop::current()->RunUntilIdle();
235 // This is still not enough to start delayed requests. We have to notify the
236 // listener that the user scripts have been updated.
237 ASSERT_FALSE(request->is_pending());
239 content::NotificationService::current()->Notify(
240 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
241 content::Source<Profile>(profile_.get()),
242 content::NotificationService::NoDetails());
243 base::MessageLoop::current()->RunUntilIdle();
244 EXPECT_EQ(kTestData, delegate.data_received());
247 TEST_F(UserScriptListenerTest, NoDelayNoExtension) {
248 net::TestDelegate delegate;
249 net::TestURLRequestContext context;
250 scoped_ptr<net::URLRequest> request(
251 StartTestRequest(&delegate, kMatchingUrl, &context));
253 // The request should be started immediately.
254 ASSERT_TRUE(request->is_pending());
256 base::MessageLoop::current()->RunUntilIdle();
257 EXPECT_EQ(kTestData, delegate.data_received());
260 TEST_F(UserScriptListenerTest, NoDelayNotMatching) {
262 base::MessageLoop::current()->RunUntilIdle();
264 net::TestDelegate delegate;
265 net::TestURLRequestContext context;
266 scoped_ptr<net::URLRequest> request(
267 StartTestRequest(&delegate, kNotMatchingUrl, &context));
269 // The request should be started immediately.
270 ASSERT_TRUE(request->is_pending());
272 base::MessageLoop::current()->RunUntilIdle();
273 EXPECT_EQ(kTestData, delegate.data_received());
276 TEST_F(UserScriptListenerTest, MultiProfile) {
278 base::MessageLoop::current()->RunUntilIdle();
280 // Fire up a second profile and have it load an extension with a content
282 TestingProfile profile2;
284 scoped_refptr<Extension> extension = LoadExtension(
285 "content_script_yahoo.json", &error);
286 ASSERT_TRUE(extension.get());
288 extensions::ExtensionRegistry::Get(&profile2)->AddEnabled(extension);
290 content::NotificationService::current()->Notify(
291 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
292 content::Source<Profile>(&profile2),
293 content::Details<Extension>(extension.get()));
295 net::TestDelegate delegate;
296 net::TestURLRequestContext context;
297 scoped_ptr<net::URLRequest> request(
298 StartTestRequest(&delegate, kMatchingUrl, &context));
299 ASSERT_FALSE(request->is_pending());
301 // When the first profile's user scripts are ready, the request should still
302 // be blocked waiting for profile2.
303 content::NotificationService::current()->Notify(
304 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
305 content::Source<Profile>(profile_.get()),
306 content::NotificationService::NoDetails());
307 base::MessageLoop::current()->RunUntilIdle();
308 ASSERT_FALSE(request->is_pending());
309 EXPECT_TRUE(delegate.data_received().empty());
311 // After profile2 is ready, the request should proceed.
312 content::NotificationService::current()->Notify(
313 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
314 content::Source<Profile>(&profile2),
315 content::NotificationService::NoDetails());
316 base::MessageLoop::current()->RunUntilIdle();
317 EXPECT_EQ(kTestData, delegate.data_received());
320 // Test when the script updated notification occurs before the throttle's
321 // WillStartRequest function is called. This can occur when there are multiple
323 TEST_F(UserScriptListenerTest, ResumeBeforeStart) {
325 base::MessageLoop::current()->RunUntilIdle();
326 net::TestDelegate delegate;
327 net::TestURLRequestContext context;
328 GURL url(kMatchingUrl);
329 scoped_ptr<net::URLRequest> request(context.CreateRequest(
330 url, net::DEFAULT_PRIORITY, &delegate, NULL));
332 ResourceThrottle* throttle =
333 listener_->CreateResourceThrottle(url, content::RESOURCE_TYPE_MAIN_FRAME);
334 ASSERT_TRUE(throttle);
335 request->SetUserData(NULL, new ThrottleController(request.get(), throttle));
337 ASSERT_FALSE(request->is_pending());
339 content::NotificationService::current()->Notify(
340 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
341 content::Source<Profile>(profile_.get()),
342 content::NotificationService::NoDetails());
343 base::MessageLoop::current()->RunUntilIdle();
346 throttle->WillStartRequest(&defer);
352 } // namespace extensions