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