1// Copyright (c) 2011 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 "base/message_loop.h"
6#include "base/threading/thread.h"
7#include "chrome/browser/extensions/extension_service_unittest.h"
8#include "chrome/browser/extensions/user_script_listener.h"
9#include "chrome/common/chrome_paths.h"
10#include "chrome/common/extensions/extension_file_util.h"
11#include "content/browser/renderer_host/global_request_id.h"
12#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
13#include "content/browser/renderer_host/resource_handler.h"
14#include "content/browser/renderer_host/resource_queue.h"
15#include "content/common/notification_service.h"
16#include "content/common/notification_type.h"
17#include "net/url_request/url_request.h"
18#include "net/url_request/url_request_test_job.h"
19#include "net/url_request/url_request_test_util.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22class Profile;
23
24namespace {
25
26const char kMatchingUrl[] = "http://google.com/";
27const char kNotMatchingUrl[] = "http://example.com/";
28const char kTestData[] = "Hello, World!";
29
30// Dummy ResourceHandler required for ResourceDispatcherHostRequestInfo.
31class DummyResourceHandler : public ResourceHandler {
32 public:
33  DummyResourceHandler() {
34  }
35
36  virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size) {
37    NOTREACHED();
38    return true;
39  }
40
41
42  virtual bool OnRequestRedirected(int request_id, const GURL& url,
43                                   ResourceResponse* response,
44                                   bool* defer) {
45    NOTREACHED();
46    return true;
47  }
48
49  virtual bool OnResponseStarted(int request_id,
50                                 ResourceResponse* response) {
51    NOTREACHED();
52    return true;
53  }
54
55  virtual bool OnWillStart(int request_id,
56                           const GURL& url,
57                           bool* defer) {
58    NOTREACHED();
59    return true;
60  }
61
62  virtual bool OnWillRead(int request_id,
63                          net::IOBuffer** buf,
64                          int* buf_size,
65                          int min_size) {
66    NOTREACHED();
67    return true;
68  }
69
70  virtual bool OnReadCompleted(int request_id, int* bytes_read) {
71    NOTREACHED();
72    return true;
73  }
74
75  virtual bool OnResponseCompleted(int request_id,
76                                   const net::URLRequestStatus& status,
77                                   const std::string& security_info) {
78    NOTREACHED();
79    return true;
80  }
81
82  virtual void OnRequestClosed() {
83  }
84
85 private:
86  DISALLOW_COPY_AND_ASSIGN(DummyResourceHandler);
87};
88
89ResourceDispatcherHostRequestInfo* CreateRequestInfo(int request_id) {
90  return new ResourceDispatcherHostRequestInfo(
91      new DummyResourceHandler(), ChildProcessInfo::RENDER_PROCESS, 0, 0,
92      request_id, ResourceType::MAIN_FRAME, 0, false, false, false);
93}
94
95// A simple test net::URLRequestJob. We don't care what it does, only that
96// whether it starts and finishes.
97class SimpleTestJob : public net::URLRequestTestJob {
98 public:
99  explicit SimpleTestJob(net::URLRequest* request)
100    : net::URLRequestTestJob(request, test_headers(), kTestData, true) {}
101 private:
102  ~SimpleTestJob() {}
103};
104
105class UserScriptListenerTest
106    : public ExtensionServiceTestBase,
107      public net::URLRequest::Interceptor {
108 public:
109  UserScriptListenerTest() {
110    net::URLRequest::RegisterRequestInterceptor(this);
111  }
112
113  ~UserScriptListenerTest() {
114    net::URLRequest::UnregisterRequestInterceptor(this);
115  }
116
117  virtual void SetUp() {
118    ExtensionServiceTestBase::SetUp();
119
120    InitializeEmptyExtensionService();
121    service_->Init();
122    MessageLoop::current()->RunAllPending();
123
124    listener_ = new UserScriptListener();
125
126    ResourceQueue::DelegateSet delegates;
127    delegates.insert(listener_.get());
128    resource_queue_.Initialize(delegates);
129  }
130
131  virtual void TearDown() {
132    resource_queue_.Shutdown();
133    listener_ = NULL;
134    MessageLoop::current()->RunAllPending();
135  }
136
137  // net::URLRequest::Interceptor
138  virtual net::URLRequestJob* MaybeIntercept(net::URLRequest* request) {
139    return new SimpleTestJob(request);
140  }
141
142 protected:
143  TestURLRequest* StartTestRequest(net::URLRequest::Delegate* delegate,
144                                   const std::string& url) {
145    TestURLRequest* request = new TestURLRequest(GURL(url), delegate);
146    scoped_ptr<ResourceDispatcherHostRequestInfo> rdh_info(
147        CreateRequestInfo(0));
148    resource_queue_.AddRequest(request, *rdh_info.get());
149    return request;
150  }
151
152  void LoadTestExtension() {
153    FilePath test_dir;
154    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
155    FilePath extension_path = test_dir
156        .AppendASCII("extensions")
157        .AppendASCII("good")
158        .AppendASCII("Extensions")
159        .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
160        .AppendASCII("1.0.0.0");
161    service_->LoadExtension(extension_path);
162  }
163
164  void UnloadTestExtension() {
165    ASSERT_FALSE(service_->extensions()->empty());
166    service_->UnloadExtension(service_->extensions()->at(0)->id(),
167                              UnloadedExtensionInfo::DISABLE);
168  }
169
170  scoped_refptr<UserScriptListener> listener_;
171
172 private:
173  ResourceQueue resource_queue_;
174};
175
176TEST_F(UserScriptListenerTest, DelayAndUpdate) {
177  LoadTestExtension();
178  MessageLoop::current()->RunAllPending();
179
180  TestDelegate delegate;
181  scoped_ptr<TestURLRequest> request(StartTestRequest(&delegate, kMatchingUrl));
182  ASSERT_FALSE(request->is_pending());
183
184  NotificationService::current()->Notify(
185      NotificationType::USER_SCRIPTS_UPDATED,
186      Source<Profile>(profile_.get()),
187      NotificationService::NoDetails());
188  MessageLoop::current()->RunAllPending();
189  EXPECT_EQ(kTestData, delegate.data_received());
190}
191
192TEST_F(UserScriptListenerTest, DelayAndUnload) {
193  LoadTestExtension();
194  MessageLoop::current()->RunAllPending();
195
196  TestDelegate delegate;
197  scoped_ptr<TestURLRequest> request(StartTestRequest(&delegate, kMatchingUrl));
198  ASSERT_FALSE(request->is_pending());
199
200  UnloadTestExtension();
201  MessageLoop::current()->RunAllPending();
202
203  // This is still not enough to start delayed requests. We have to notify the
204  // listener that the user scripts have been updated.
205  ASSERT_FALSE(request->is_pending());
206
207  NotificationService::current()->Notify(
208      NotificationType::USER_SCRIPTS_UPDATED,
209      Source<Profile>(profile_.get()),
210      NotificationService::NoDetails());
211  MessageLoop::current()->RunAllPending();
212  EXPECT_EQ(kTestData, delegate.data_received());
213}
214
215TEST_F(UserScriptListenerTest, NoDelayNoExtension) {
216  TestDelegate delegate;
217  scoped_ptr<TestURLRequest> request(StartTestRequest(&delegate, kMatchingUrl));
218
219  // The request should be started immediately.
220  ASSERT_TRUE(request->is_pending());
221
222  MessageLoop::current()->RunAllPending();
223  EXPECT_EQ(kTestData, delegate.data_received());
224}
225
226TEST_F(UserScriptListenerTest, NoDelayNotMatching) {
227  LoadTestExtension();
228  MessageLoop::current()->RunAllPending();
229
230  TestDelegate delegate;
231  scoped_ptr<TestURLRequest> request(StartTestRequest(&delegate,
232                                                      kNotMatchingUrl));
233
234  // The request should be started immediately.
235  ASSERT_TRUE(request->is_pending());
236
237  MessageLoop::current()->RunAllPending();
238  EXPECT_EQ(kTestData, delegate.data_received());
239}
240
241}  // namespace
242