• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "chrome/browser/extensions/user_script_master.h"
6 
7 #include <string>
8 
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/message_loop.h"
12 #include "base/path_service.h"
13 #include "base/string_util.h"
14 #include "base/memory/scoped_temp_dir.h"
15 #include "chrome/test/testing_profile.h"
16 #include "content/browser/browser_thread.h"
17 #include "content/common/notification_registrar.h"
18 #include "content/common/notification_service.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 // Test bringing up a master on a specific directory, putting a script
22 // in there, etc.
23 
24 class UserScriptMasterTest : public testing::Test,
25                              public NotificationObserver {
26  public:
UserScriptMasterTest()27   UserScriptMasterTest()
28       : message_loop_(MessageLoop::TYPE_UI),
29         shared_memory_(NULL) {
30   }
31 
SetUp()32   virtual void SetUp() {
33     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
34 
35     // Register for all user script notifications.
36     registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED,
37                    NotificationService::AllSources());
38 
39     // UserScriptMaster posts tasks to the file thread so make the current
40     // thread look like one.
41     file_thread_.reset(new BrowserThread(
42         BrowserThread::FILE, MessageLoop::current()));
43   }
44 
TearDown()45   virtual void TearDown() {
46     file_thread_.reset();
47   }
48 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)49   virtual void Observe(NotificationType type,
50                        const NotificationSource& source,
51                        const NotificationDetails& details) {
52     DCHECK(type == NotificationType::USER_SCRIPTS_UPDATED);
53 
54     shared_memory_ = Details<base::SharedMemory>(details).ptr();
55     if (MessageLoop::current() == &message_loop_)
56       MessageLoop::current()->Quit();
57   }
58 
59   // Directory containing user scripts.
60   ScopedTempDir temp_dir_;
61 
62   NotificationRegistrar registrar_;
63 
64   // MessageLoop used in tests.
65   MessageLoop message_loop_;
66 
67   scoped_ptr<BrowserThread> file_thread_;
68 
69   // Updated to the script shared memory when we get notified.
70   base::SharedMemory* shared_memory_;
71 };
72 
73 // Test that we get notified even when there are no scripts.
TEST_F(UserScriptMasterTest,NoScripts)74 TEST_F(UserScriptMasterTest, NoScripts) {
75   TestingProfile profile;
76   scoped_refptr<UserScriptMaster> master(new UserScriptMaster(temp_dir_.path(),
77                                                               &profile));
78   master->StartScan();
79   message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask);
80   message_loop_.Run();
81 
82   ASSERT_TRUE(shared_memory_ != NULL);
83 }
84 
85 // Test that we get notified about scripts if they're already in the test dir.
TEST_F(UserScriptMasterTest,ExistingScripts)86 TEST_F(UserScriptMasterTest, ExistingScripts) {
87   TestingProfile profile;
88   FilePath path = temp_dir_.path().AppendASCII("script.user.js");
89 
90   const char content[] = "some content";
91   size_t written = file_util::WriteFile(path, content, sizeof(content));
92   ASSERT_EQ(written, sizeof(content));
93 
94   scoped_refptr<UserScriptMaster> master(new UserScriptMaster(temp_dir_.path(),
95                                                               &profile));
96   master->StartScan();
97 
98   message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask);
99   message_loop_.Run();
100 
101   ASSERT_TRUE(shared_memory_ != NULL);
102 }
103 
TEST_F(UserScriptMasterTest,Parse1)104 TEST_F(UserScriptMasterTest, Parse1) {
105   const std::string text(
106     "// This is my awesome script\n"
107     "// It does stuff.\n"
108     "// ==UserScript==   trailing garbage\n"
109     "// @name foobar script\n"
110     "// @namespace http://www.google.com/\n"
111     "// @include *mail.google.com*\n"
112     "// \n"
113     "// @othergarbage\n"
114     "// @include *mail.yahoo.com*\r\n"
115     "// @include  \t *mail.msn.com*\n" // extra spaces after "@include" OK
116     "//@include not-recognized\n" // must have one space after "//"
117     "// ==/UserScript==  trailing garbage\n"
118     "\n"
119     "\n"
120     "alert('hoo!');\n");
121 
122   UserScript script;
123   EXPECT_TRUE(UserScriptMaster::ScriptReloader::ParseMetadataHeader(
124       text, &script));
125   ASSERT_EQ(3U, script.globs().size());
126   EXPECT_EQ("*mail.google.com*", script.globs()[0]);
127   EXPECT_EQ("*mail.yahoo.com*", script.globs()[1]);
128   EXPECT_EQ("*mail.msn.com*", script.globs()[2]);
129 }
130 
TEST_F(UserScriptMasterTest,Parse2)131 TEST_F(UserScriptMasterTest, Parse2) {
132   const std::string text("default to @include *");
133 
134   UserScript script;
135   EXPECT_TRUE(UserScriptMaster::ScriptReloader::ParseMetadataHeader(
136       text, &script));
137   ASSERT_EQ(1U, script.globs().size());
138   EXPECT_EQ("*", script.globs()[0]);
139 }
140 
TEST_F(UserScriptMasterTest,Parse3)141 TEST_F(UserScriptMasterTest, Parse3) {
142   const std::string text(
143     "// ==UserScript==\n"
144     "// @include *foo*\n"
145     "// ==/UserScript=="); // no trailing newline
146 
147   UserScript script;
148   UserScriptMaster::ScriptReloader::ParseMetadataHeader(text, &script);
149   ASSERT_EQ(1U, script.globs().size());
150   EXPECT_EQ("*foo*", script.globs()[0]);
151 }
152 
TEST_F(UserScriptMasterTest,Parse4)153 TEST_F(UserScriptMasterTest, Parse4) {
154   const std::string text(
155     "// ==UserScript==\n"
156     "// @match http://*.mail.google.com/*\n"
157     "// @match  \t http://mail.yahoo.com/*\n"
158     "// ==/UserScript==\n");
159 
160   UserScript script;
161   EXPECT_TRUE(UserScriptMaster::ScriptReloader::ParseMetadataHeader(
162       text, &script));
163   EXPECT_EQ(0U, script.globs().size());
164   ASSERT_EQ(2U, script.url_patterns().size());
165   EXPECT_EQ("http://*.mail.google.com/*",
166             script.url_patterns()[0].GetAsString());
167   EXPECT_EQ("http://mail.yahoo.com/*",
168             script.url_patterns()[1].GetAsString());
169 }
170 
TEST_F(UserScriptMasterTest,Parse5)171 TEST_F(UserScriptMasterTest, Parse5) {
172   const std::string text(
173     "// ==UserScript==\n"
174     "// @match http://*mail.google.com/*\n"
175     "// ==/UserScript==\n");
176 
177   // Invalid @match value.
178   UserScript script;
179   EXPECT_FALSE(UserScriptMaster::ScriptReloader::ParseMetadataHeader(
180       text, &script));
181 }
182 
TEST_F(UserScriptMasterTest,Parse6)183 TEST_F(UserScriptMasterTest, Parse6) {
184   const std::string text(
185     "// ==UserScript==\n"
186     "// @include http://*.mail.google.com/*\n"
187     "// @match  \t http://mail.yahoo.com/*\n"
188     "// ==/UserScript==\n");
189 
190   // Allowed to match @include and @match.
191   UserScript script;
192   EXPECT_TRUE(UserScriptMaster::ScriptReloader::ParseMetadataHeader(
193       text, &script));
194 }
195 
TEST_F(UserScriptMasterTest,Parse7)196 TEST_F(UserScriptMasterTest, Parse7) {
197   // Greasemonkey allows there to be any leading text before the comment marker.
198   const std::string text(
199     "// ==UserScript==\n"
200     "adsasdfasf// @name hello\n"
201     "  // @description\twiggity woo\n"
202     "\t// @match  \t http://mail.yahoo.com/*\n"
203     "// ==/UserScript==\n");
204 
205   UserScript script;
206   EXPECT_TRUE(UserScriptMaster::ScriptReloader::ParseMetadataHeader(
207       text, &script));
208   ASSERT_EQ("hello", script.name());
209   ASSERT_EQ("wiggity woo", script.description());
210   ASSERT_EQ(1U, script.url_patterns().size());
211   EXPECT_EQ("http://mail.yahoo.com/*",
212             script.url_patterns()[0].GetAsString());
213 }
214 
TEST_F(UserScriptMasterTest,SkipBOMAtTheBeginning)215 TEST_F(UserScriptMasterTest, SkipBOMAtTheBeginning) {
216   FilePath path = temp_dir_.path().AppendASCII("script.user.js");
217 
218   const std::string content(
219     "\xEF\xBB\xBF// ==UserScript==\n"
220     "// @match http://*.mail.google.com/*\n"
221     "// ==/UserScript==\n");
222   size_t written = file_util::WriteFile(path, content.c_str(), content.size());
223   ASSERT_EQ(written, content.size());
224 
225   UserScriptList script_list;
226   UserScriptMaster::ScriptReloader::LoadScriptsFromDirectory(
227       temp_dir_.path(), &script_list);
228   ASSERT_EQ(1U, script_list.size());
229 
230   EXPECT_EQ(content.substr(3),
231             script_list[0].js_scripts()[0].GetContent().as_string());
232   EXPECT_EQ("http://*.mail.google.com/*",
233             script_list[0].url_patterns()[0].GetAsString());
234 }
235 
TEST_F(UserScriptMasterTest,LeaveBOMNotAtTheBeginning)236 TEST_F(UserScriptMasterTest, LeaveBOMNotAtTheBeginning) {
237   FilePath path = temp_dir_.path().AppendASCII("script.user.js");
238 
239   const std::string content(
240     "// ==UserScript==\n"
241     "// @match http://*.mail.google.com/*\n"
242     "// ==/UserScript==\n"
243     "// @bom \xEF\xBB\xBF");
244   size_t written = file_util::WriteFile(path, content.c_str(), content.size());
245   ASSERT_EQ(written, content.size());
246 
247   UserScriptList script_list;
248   UserScriptMaster::ScriptReloader::LoadScriptsFromDirectory(
249       temp_dir_.path(), &script_list);
250   ASSERT_EQ(1U, script_list.size());
251 
252   EXPECT_EQ(content, script_list[0].js_scripts()[0].GetContent().as_string());
253   EXPECT_EQ("http://*.mail.google.com/*",
254             script_list[0].url_patterns()[0].GetAsString());
255 }
256