1 // Copyright 2014 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 "extensions/browser/extension_user_script_loader.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/location.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_util.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "content/public/browser/notification_observer.h"
19 #include "content/public/browser/notification_registrar.h"
20 #include "content/public/browser/notification_service.h"
21 #include "content/public/test/test_browser_thread.h"
22 #include "extensions/common/host_id.h"
23 #include "testing/gtest/include/gtest/gtest.h"
25 using content::BrowserThread
;
26 using extensions::URLPatternSet
;
30 static void AddPattern(URLPatternSet
* extent
, const std::string
& pattern
) {
31 int schemes
= URLPattern::SCHEME_ALL
;
32 extent
->AddPattern(URLPattern(schemes
, pattern
));
36 namespace extensions
{
38 // Test bringing up a script loader on a specific directory, putting a script
41 class ExtensionUserScriptLoaderTest
: public testing::Test
,
42 public content::NotificationObserver
{
44 ExtensionUserScriptLoaderTest() : shared_memory_(NULL
) {}
46 void SetUp() override
{
47 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
49 // Register for all user script notifications.
51 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED
,
52 content::NotificationService::AllSources());
54 // ExtensionUserScriptLoader posts tasks to the file thread so make the
55 // current thread look like one.
56 file_thread_
.reset(new content::TestBrowserThread(
57 BrowserThread::FILE, base::MessageLoop::current()));
58 ui_thread_
.reset(new content::TestBrowserThread(
59 BrowserThread::UI
, base::MessageLoop::current()));
62 void TearDown() override
{
67 void Observe(int type
,
68 const content::NotificationSource
& source
,
69 const content::NotificationDetails
& details
) override
{
70 DCHECK(type
== extensions::NOTIFICATION_USER_SCRIPTS_UPDATED
);
72 shared_memory_
= content::Details
<base::SharedMemory
>(details
).ptr();
73 if (base::MessageLoop::current() == &message_loop_
)
74 base::MessageLoop::current()->Quit();
77 // Directory containing user scripts.
78 base::ScopedTempDir temp_dir_
;
80 content::NotificationRegistrar registrar_
;
82 // MessageLoop used in tests.
83 base::MessageLoopForUI message_loop_
;
85 scoped_ptr
<content::TestBrowserThread
> file_thread_
;
86 scoped_ptr
<content::TestBrowserThread
> ui_thread_
;
88 // Updated to the script shared memory when we get notified.
89 base::SharedMemory
* shared_memory_
;
92 // Test that we get notified even when there are no scripts.
93 TEST_F(ExtensionUserScriptLoaderTest
, NoScripts
) {
94 TestingProfile profile
;
95 ExtensionUserScriptLoader
loader(
98 true /* listen_for_extension_system_loaded */);
100 message_loop_
.task_runner()->PostTask(FROM_HERE
,
101 base::MessageLoop::QuitClosure());
104 ASSERT_TRUE(shared_memory_
!= NULL
);
107 TEST_F(ExtensionUserScriptLoaderTest
, Parse1
) {
108 const std::string
text(
109 "// This is my awesome script\n"
110 "// It does stuff.\n"
111 "// ==UserScript== trailing garbage\n"
112 "// @name foobar script\n"
113 "// @namespace http://www.google.com/\n"
114 "// @include *mail.google.com*\n"
117 "// @include *mail.yahoo.com*\r\n"
118 "// @include \t *mail.msn.com*\n" // extra spaces after "@include" OK
119 "//@include not-recognized\n" // must have one space after "//"
120 "// ==/UserScript== trailing garbage\n"
126 EXPECT_TRUE(ExtensionUserScriptLoader::ParseMetadataHeader(text
, &script
));
127 ASSERT_EQ(3U, script
.globs().size());
128 EXPECT_EQ("*mail.google.com*", script
.globs()[0]);
129 EXPECT_EQ("*mail.yahoo.com*", script
.globs()[1]);
130 EXPECT_EQ("*mail.msn.com*", script
.globs()[2]);
133 TEST_F(ExtensionUserScriptLoaderTest
, Parse2
) {
134 const std::string
text("default to @include *");
137 EXPECT_TRUE(ExtensionUserScriptLoader::ParseMetadataHeader(text
, &script
));
138 ASSERT_EQ(1U, script
.globs().size());
139 EXPECT_EQ("*", script
.globs()[0]);
142 TEST_F(ExtensionUserScriptLoaderTest
, Parse3
) {
143 const std::string
text(
144 "// ==UserScript==\n"
145 "// @include *foo*\n"
146 "// ==/UserScript=="); // no trailing newline
149 ExtensionUserScriptLoader::ParseMetadataHeader(text
, &script
);
150 ASSERT_EQ(1U, script
.globs().size());
151 EXPECT_EQ("*foo*", script
.globs()[0]);
154 TEST_F(ExtensionUserScriptLoaderTest
, Parse4
) {
155 const std::string
text(
156 "// ==UserScript==\n"
157 "// @match http://*.mail.google.com/*\n"
158 "// @match \t http://mail.yahoo.com/*\n"
159 "// ==/UserScript==\n");
161 URLPatternSet expected_patterns
;
162 AddPattern(&expected_patterns
, "http://*.mail.google.com/*");
163 AddPattern(&expected_patterns
, "http://mail.yahoo.com/*");
166 EXPECT_TRUE(ExtensionUserScriptLoader::ParseMetadataHeader(text
, &script
));
167 EXPECT_EQ(0U, script
.globs().size());
168 EXPECT_EQ(expected_patterns
, script
.url_patterns());
171 TEST_F(ExtensionUserScriptLoaderTest
, Parse5
) {
172 const std::string
text(
173 "// ==UserScript==\n"
174 "// @match http://*mail.google.com/*\n"
175 "// ==/UserScript==\n");
177 // Invalid @match value.
179 EXPECT_FALSE(ExtensionUserScriptLoader::ParseMetadataHeader(text
, &script
));
182 TEST_F(ExtensionUserScriptLoaderTest
, Parse6
) {
183 const std::string
text(
184 "// ==UserScript==\n"
185 "// @include http://*.mail.google.com/*\n"
186 "// @match \t http://mail.yahoo.com/*\n"
187 "// ==/UserScript==\n");
189 // Allowed to match @include and @match.
191 EXPECT_TRUE(ExtensionUserScriptLoader::ParseMetadataHeader(text
, &script
));
194 TEST_F(ExtensionUserScriptLoaderTest
, Parse7
) {
195 // Greasemonkey allows there to be any leading text before the comment marker.
196 const std::string
text(
197 "// ==UserScript==\n"
198 "adsasdfasf// @name hello\n"
199 " // @description\twiggity woo\n"
200 "\t// @match \t http://mail.yahoo.com/*\n"
201 "// ==/UserScript==\n");
204 EXPECT_TRUE(ExtensionUserScriptLoader::ParseMetadataHeader(text
, &script
));
205 ASSERT_EQ("hello", script
.name());
206 ASSERT_EQ("wiggity woo", script
.description());
207 ASSERT_EQ(1U, script
.url_patterns().patterns().size());
208 EXPECT_EQ("http://mail.yahoo.com/*",
209 script
.url_patterns().begin()->GetAsString());
212 TEST_F(ExtensionUserScriptLoaderTest
, Parse8
) {
213 const std::string
text(
214 "// ==UserScript==\n"
215 "// @name myscript\n"
216 "// @match http://www.google.com/*\n"
217 "// @exclude_match http://www.google.com/foo*\n"
218 "// ==/UserScript==\n");
221 EXPECT_TRUE(ExtensionUserScriptLoader::ParseMetadataHeader(text
, &script
));
222 ASSERT_EQ("myscript", script
.name());
223 ASSERT_EQ(1U, script
.url_patterns().patterns().size());
224 EXPECT_EQ("http://www.google.com/*",
225 script
.url_patterns().begin()->GetAsString());
226 ASSERT_EQ(1U, script
.exclude_url_patterns().patterns().size());
227 EXPECT_EQ("http://www.google.com/foo*",
228 script
.exclude_url_patterns().begin()->GetAsString());
231 TEST_F(ExtensionUserScriptLoaderTest
, SkipBOMAtTheBeginning
) {
232 base::FilePath path
= temp_dir_
.path().AppendASCII("script.user.js");
233 const std::string
content("\xEF\xBB\xBF alert('hello');");
234 size_t written
= base::WriteFile(path
, content
.c_str(), content
.size());
235 ASSERT_EQ(written
, content
.size());
237 UserScript user_script
;
238 user_script
.js_scripts().push_back(
239 UserScript::File(temp_dir_
.path(), path
.BaseName(), GURL()));
241 UserScriptList user_scripts
;
242 user_scripts
.push_back(user_script
);
244 TestingProfile profile
;
245 ExtensionUserScriptLoader
loader(
248 true /* listen_for_extension_system_loaded */);
249 loader
.LoadScriptsForTest(&user_scripts
);
251 EXPECT_EQ(content
.substr(3),
252 user_scripts
[0].js_scripts()[0].GetContent().as_string());
255 TEST_F(ExtensionUserScriptLoaderTest
, LeaveBOMNotAtTheBeginning
) {
256 base::FilePath path
= temp_dir_
.path().AppendASCII("script.user.js");
257 const std::string
content("alert('here's a BOOM: \xEF\xBB\xBF');");
258 size_t written
= base::WriteFile(path
, content
.c_str(), content
.size());
259 ASSERT_EQ(written
, content
.size());
261 UserScript user_script
;
262 user_script
.js_scripts().push_back(UserScript::File(
263 temp_dir_
.path(), path
.BaseName(), GURL()));
265 UserScriptList user_scripts
;
266 user_scripts
.push_back(user_script
);
268 TestingProfile profile
;
269 ExtensionUserScriptLoader
loader(
272 true /* listen_for_extension_system_loaded */);
273 loader
.LoadScriptsForTest(&user_scripts
);
275 EXPECT_EQ(content
, user_scripts
[0].js_scripts()[0].GetContent().as_string());
278 } // namespace extensions