Home | History | Annotate | Download | only in extensions
      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