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: 27 UserScriptMasterTest() 28 : message_loop_(MessageLoop::TYPE_UI), 29 shared_memory_(NULL) { 30 } 31 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 45 virtual void TearDown() { 46 file_thread_.reset(); 47 } 48 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. 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. 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 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 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 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 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 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 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 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 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 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