From 0938d3cb82dfd2424117f73573e757424ebede00 Mon Sep 17 00:00:00 2001 From: "aa@chromium.org" Date: Fri, 9 Jan 2009 20:37:35 +0000 Subject: This is a rename of the term 'Greasemonkey' to 'user script' in Chromium. I'm doing this to avoid confusion with the Firefox version of Greasemonkey and also because 'user script' is really the correct generic term. At the same time, I also moved user_script_master* into extensions/ because I want these two pieces to get closer and closer such that standalone user scripts are just a very small extension. Also extensions will be relying on most of the user script code. Review URL: http://codereview.chromium.org/17281 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7827 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/browser.scons | 2 +- chrome/browser/browser.vcproj | 16 +- chrome/browser/extensions/user_script_master.cc | 219 +++++++++++++++++++++ chrome/browser/extensions/user_script_master.h | 76 +++++++ .../extensions/user_script_master_unittest.cc | 118 +++++++++++ chrome/browser/greasemonkey_master.cc | 219 --------------------- chrome/browser/greasemonkey_master.h | 76 ------- chrome/browser/greasemonkey_master_unittest.cc | 118 ----------- chrome/browser/net/chrome_url_request_context.cc | 4 +- chrome/browser/profile.cc | 18 +- chrome/browser/profile.h | 12 +- chrome/browser/render_process_host.cc | 32 ++- chrome/browser/render_process_host.h | 10 +- chrome/chrome.xcodeproj/project.pbxproj | 32 +-- chrome/common/chrome_switches.cc | 4 +- chrome/common/chrome_switches.h | 2 +- chrome/common/notification_types.h | 4 +- chrome/common/render_messages_internal.h | 4 +- chrome/renderer/greasemonkey_slave.cc | 170 ---------------- chrome/renderer/greasemonkey_slave.h | 95 --------- chrome/renderer/greasemonkey_slave_unittest.cc | 71 ------- chrome/renderer/render_thread.cc | 18 +- chrome/renderer/render_thread.h | 10 +- chrome/renderer/render_view.cc | 16 +- chrome/renderer/render_view.h | 4 +- chrome/renderer/renderer.scons | 2 +- chrome/renderer/renderer.vcproj | 16 +- chrome/renderer/user_script_slave.cc | 170 ++++++++++++++++ chrome/renderer/user_script_slave.h | 95 +++++++++ chrome/renderer/user_script_slave_unittest.cc | 71 +++++++ chrome/test/testing_profile.h | 2 +- chrome/test/unit/unit_tests.scons | 4 +- chrome/test/unit/unittests.vcproj | 16 +- 33 files changed, 860 insertions(+), 866 deletions(-) create mode 100644 chrome/browser/extensions/user_script_master.cc create mode 100644 chrome/browser/extensions/user_script_master.h create mode 100644 chrome/browser/extensions/user_script_master_unittest.cc delete mode 100644 chrome/browser/greasemonkey_master.cc delete mode 100644 chrome/browser/greasemonkey_master.h delete mode 100644 chrome/browser/greasemonkey_master_unittest.cc delete mode 100644 chrome/renderer/greasemonkey_slave.cc delete mode 100644 chrome/renderer/greasemonkey_slave.h delete mode 100644 chrome/renderer/greasemonkey_slave_unittest.cc create mode 100644 chrome/renderer/user_script_slave.cc create mode 100644 chrome/renderer/user_script_slave.h create mode 100644 chrome/renderer/user_script_slave_unittest.cc diff --git a/chrome/browser/browser.scons b/chrome/browser/browser.scons index 3cebdb8..e531153 100644 --- a/chrome/browser/browser.scons +++ b/chrome/browser/browser.scons @@ -60,9 +60,9 @@ if not env.Bit('mac'): 'download/save_file.cc', 'extensions/extension.cc', 'extensions/extensions_service.cc', + 'extensions/user_script_master.cc', 'google_url_tracker.cc', 'google_util.cc', - 'greasemonkey_master.cc', 'history/archived_database.cc', 'history/download_database.cc', 'history/expire_history_backend.cc', diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index 644f7b0..a8cab66 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -2261,6 +2261,14 @@ RelativePath=".\extensions\extensions_service.h" > + + + + - - - - diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc new file mode 100644 index 0000000..dcd912a --- /dev/null +++ b/chrome/browser/extensions/user_script_master.cc @@ -0,0 +1,219 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/user_script_master.h" + +#include + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/pickle.h" +#include "base/string_util.h" +#include "chrome/common/notification_service.h" +#include "googleurl/src/gurl.h" +#include "net/base/net_util.h" + +// We reload user scripts on the file thread to prevent blocking the UI. +// ScriptReloader lives on the file thread and does the reload +// work, and then sends a message back to its master with a new SharedMemory*. + +// ScriptReloader is the worker that manages running the script scan +// on the file thread. +// It must be created on, and its public API must only be called from, +// the master's thread. +class UserScriptMaster::ScriptReloader + : public base::RefCounted { + public: + ScriptReloader(UserScriptMaster* master) + : master_(master), master_message_loop_(MessageLoop::current()) {} + + // Start a scan for scripts. + // Will always send a message to the master upon completion. + void StartScan(MessageLoop* work_loop, const FilePath& script_dir); + + // The master is going away; don't call it back. + void DisownMaster() { + master_ = NULL; + } + + private: + // Where functions are run: + // master file + // StartScan -> RunScan + // GetNewScripts() + // NotifyMaster <- RunScan + + // Runs on the master thread. + // Notify the master that new scripts are available. + void NotifyMaster(base::SharedMemory* memory); + + // Runs on the File thread. + // Scan the script directory for scripts, calling NotifyMaster when done. + // The path is intentionally passed by value so its lifetime isn't tied + // to the caller. + void RunScan(const FilePath script_dir); + + // Runs on the File thread. + // Scan the script directory for scripts, returning either a new SharedMemory + // or NULL on error. + base::SharedMemory* GetNewScripts(const FilePath& script_dir); + + // A pointer back to our master. + // May be NULL if DisownMaster() is called. + UserScriptMaster* master_; + + // The message loop to call our master back on. + // Expected to always outlive us. + MessageLoop* master_message_loop_; + + DISALLOW_COPY_AND_ASSIGN(ScriptReloader); +}; + +void UserScriptMaster::ScriptReloader::StartScan( + MessageLoop* work_loop, + const FilePath& script_dir) { + // Add a reference to ourselves to keep ourselves alive while we're running. + // Balanced by NotifyMaster(). + AddRef(); + work_loop->PostTask(FROM_HERE, + NewRunnableMethod(this, + &UserScriptMaster::ScriptReloader::RunScan, + script_dir)); +} + +void UserScriptMaster::ScriptReloader::NotifyMaster( + base::SharedMemory* memory) { + if (!master_) { + // The master went away, so these new scripts aren't useful anymore. + delete memory; + } else { + master_->NewScriptsAvailable(memory); + } + + // Drop our self-reference. + // Balances StartScan(). + Release(); +} + +void UserScriptMaster::ScriptReloader::RunScan(const FilePath script_dir) { + base::SharedMemory* shared_memory = GetNewScripts(script_dir); + + // Post the new scripts back to the master's message loop. + master_message_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, + &UserScriptMaster::ScriptReloader::NotifyMaster, + shared_memory)); +} + +base::SharedMemory* UserScriptMaster::ScriptReloader::GetNewScripts( + const FilePath& script_dir) { + std::vector scripts; + + file_util::FileEnumerator enumerator(script_dir, false, + file_util::FileEnumerator::FILES, + FILE_PATH_LITERAL("*.user.js")); + for (FilePath file = enumerator.Next(); !file.value().empty(); + file = enumerator.Next()) { + scripts.push_back(file.ToWStringHack()); + } + + if (scripts.empty()) + return NULL; + + // Pickle scripts data. + Pickle pickle; + pickle.WriteSize(scripts.size()); + for (std::vector::iterator path = scripts.begin(); + path != scripts.end(); ++path) { + std::string file_url = net::FilePathToFileURL(*path).spec(); + std::string contents; + // TODO(aa): Support unicode script files. + file_util::ReadFileToString(*path, &contents); + + // Write scripts as 'data' so that we can read it out in the slave without + // allocating a new string. + pickle.WriteData(file_url.c_str(), file_url.length()); + pickle.WriteData(contents.c_str(), contents.length()); + } + + // Create the shared memory object. + scoped_ptr shared_memory(new base::SharedMemory()); + + if (!shared_memory->Create(std::wstring(), // anonymous + false, // read-only + false, // open existing + pickle.size())) { + return NULL; + } + + // Map into our process. + if (!shared_memory->Map(pickle.size())) + return NULL; + + // Copy the pickle to shared memory. + memcpy(shared_memory->memory(), pickle.data(), pickle.size()); + + return shared_memory.release(); +} + + +UserScriptMaster::UserScriptMaster(MessageLoop* worker_loop, + const FilePath& script_dir) + : user_script_dir_(new FilePath(script_dir)), + dir_watcher_(new DirectoryWatcher), + worker_loop_(worker_loop), + pending_scan_(false) { + // Watch our scripts directory for modifications. + if (dir_watcher_->Watch(script_dir, this)) { + // (Asynchronously) scan for our initial set of scripts. + StartScan(); + } +} + +UserScriptMaster::~UserScriptMaster() { + if (script_reloader_) + script_reloader_->DisownMaster(); +} + +void UserScriptMaster::NewScriptsAvailable(base::SharedMemory* handle) { + // Ensure handle is deleted or released. + scoped_ptr handle_deleter(handle); + + if (pending_scan_) { + // While we were scanning, there were further changes. Don't bother + // notifying about these scripts and instead just immediately rescan. + pending_scan_ = false; + StartScan(); + } else { + // We're no longer scanning. + script_reloader_ = NULL; + // We've got scripts ready to go. + shared_memory_.swap(handle_deleter); + + NotificationService::current()->Notify(NOTIFY_USER_SCRIPTS_LOADED, + NotificationService::AllSources(), + Details(handle)); + } +} + +void UserScriptMaster::OnDirectoryChanged(const FilePath& path) { + if (script_reloader_.get()) { + // We're already scanning for scripts. We note that we should rescan when + // we get the chance. + pending_scan_ = true; + return; + } + + StartScan(); +} + +void UserScriptMaster::StartScan() { + if (!script_reloader_) + script_reloader_ = new ScriptReloader(this); + + script_reloader_->StartScan(worker_loop_, *user_script_dir_); +} diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h new file mode 100644 index 0000000..4798bb2 --- /dev/null +++ b/chrome/browser/extensions/user_script_master.h @@ -0,0 +1,76 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_MASTER_H_ +#define CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_MASTER_H_ + +#include "base/directory_watcher.h" +#include "base/file_path.h" +#include "base/process.h" +#include "base/scoped_ptr.h" +#include "base/shared_memory.h" + +class MessageLoop; + +// Manages a segment of shared memory that contains the user scripts the user +// has installed. Lives on the UI thread. +class UserScriptMaster : public base::RefCounted, + public DirectoryWatcher::Delegate { + public: + // For testability, the constructor takes the MessageLoop to run the + // script-reloading worker on as well as the path the scripts live in. + // These are normally the file thread and a directory inside the profile. + UserScriptMaster(MessageLoop* worker, const FilePath& script_dir); + ~UserScriptMaster(); + + // Gets the segment of shared memory for the scripts. + base::SharedMemory* GetSharedMemory() const { + return shared_memory_.get(); + } + + // Called by the script reloader when new scripts have been loaded. + void NewScriptsAvailable(base::SharedMemory* handle); + + // Return true if we have any scripts ready. + bool ScriptsReady() const { return shared_memory_.get() != NULL; } + + // Returns the path to the directory user scripts are stored in. + FilePath user_script_dir() const { return *user_script_dir_; } + + private: + class ScriptReloader; + + // DirectoryWatcher::Delegate implementation. + virtual void OnDirectoryChanged(const FilePath& path); + + // Kicks off a process on the file thread to reload scripts from disk + // into a new chunk of shared memory and notify renderers. + void StartScan(); + + // The directory containing user scripts. + scoped_ptr user_script_dir_; + + // The watcher watches the profile's user scripts directory for new scripts. + scoped_ptr dir_watcher_; + + // The MessageLoop that the scanner worker runs on. + // Typically the file thread; configurable for testing. + MessageLoop* worker_loop_; + + // ScriptReloader (in another thread) reloads script off disk. + // We hang on to our pointer to know if we've already got one running. + scoped_refptr script_reloader_; + + // Contains the scripts that were found the last time scripts were updated. + scoped_ptr shared_memory_; + + // If the script directory is modified while we're rescanning it, we note + // that we're currently mid-scan and then start over again once the scan + // finishes. This boolean tracks whether another scan is pending. + bool pending_scan_; + + DISALLOW_COPY_AND_ASSIGN(UserScriptMaster); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_MASTER_H_ diff --git a/chrome/browser/extensions/user_script_master_unittest.cc b/chrome/browser/extensions/user_script_master_unittest.cc new file mode 100644 index 0000000..c630bbd --- /dev/null +++ b/chrome/browser/extensions/user_script_master_unittest.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/user_script_master.h" + +#include + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/common/notification_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Test bringing up a master on a specific directory, putting a script in there, etc. + +class UserScriptMasterTest : public testing::Test, + public NotificationObserver { + public: + UserScriptMasterTest() : shared_memory_(NULL) {} + + virtual void SetUp() { + // Name a subdirectory of the temp directory. + std::wstring path_str; + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &path_str)); + script_dir_ = FilePath(path_str).Append( + FILE_PATH_LITERAL("UserScriptTest")); + + // Create a fresh, empty copy of this directory. + file_util::Delete(script_dir_.value(), true); + file_util::CreateDirectory(script_dir_.value()); + + // Register for all user script notifications. + NotificationService::current()->AddObserver(this, + NOTIFY_USER_SCRIPTS_LOADED, + NotificationService::AllSources()); + } + + virtual void TearDown() { + NotificationService::current()->RemoveObserver(this, + NOTIFY_USER_SCRIPTS_LOADED, + NotificationService::AllSources()); + + // Clean up test directory. + ASSERT_TRUE(file_util::Delete(script_dir_.value(), true)); + ASSERT_FALSE(file_util::PathExists(script_dir_.value())); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NOTIFY_USER_SCRIPTS_LOADED); + + shared_memory_ = Details(details).ptr(); + if (MessageLoop::current() == &message_loop_) + MessageLoop::current()->Quit(); + } + + // MessageLoop used in tests. + MessageLoop message_loop_; + + // Directory containing user scripts. + FilePath script_dir_; + + // Updated to the script shared memory when we get notified. + base::SharedMemory* shared_memory_; +}; + +// Test that we *don't* get spurious notifications. +TEST_F(UserScriptMasterTest, NoScripts) { + // Set shared_memory_ to something non-NULL, so we can check it became NULL. + shared_memory_ = reinterpret_cast(1); + + scoped_refptr master( + new UserScriptMaster(MessageLoop::current(), script_dir_)); + message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); + message_loop_.Run(); + + // There were no scripts in the script dir, so we shouldn't have gotten + // a notification. + ASSERT_EQ(NULL, shared_memory_); +} + +// Test that we get notified about new scripts after they're added. +TEST_F(UserScriptMasterTest, NewScripts) { + scoped_refptr master( + new UserScriptMaster(MessageLoop::current(), script_dir_)); + + FilePath path = script_dir_.Append(FILE_PATH_LITERAL("script.user.js")); + + std::ofstream file; + file.open(WideToUTF8(path.value()).c_str()); + file << "some content"; + file.close(); + + message_loop_.Run(); + + ASSERT_TRUE(shared_memory_ != NULL); +} + +// Test that we get notified about scripts if they're already in the test dir. +TEST_F(UserScriptMasterTest, ExistingScripts) { + FilePath path = script_dir_.Append(FILE_PATH_LITERAL("script.user.js")); + std::ofstream file; + file.open(WideToUTF8(path.value()).c_str()); + file << "some content"; + file.close(); + + scoped_refptr master( + new UserScriptMaster(MessageLoop::current(), script_dir_)); + + message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); + message_loop_.Run(); + + ASSERT_TRUE(shared_memory_ != NULL); +} diff --git a/chrome/browser/greasemonkey_master.cc b/chrome/browser/greasemonkey_master.cc deleted file mode 100644 index 715da54..0000000 --- a/chrome/browser/greasemonkey_master.cc +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/greasemonkey_master.h" - -#include - -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/path_service.h" -#include "base/pickle.h" -#include "base/string_util.h" -#include "chrome/common/notification_service.h" -#include "googleurl/src/gurl.h" -#include "net/base/net_util.h" - -// We reload user scripts on the file thread to prevent blocking the UI. -// ScriptReloader lives on the file thread and does the reload -// work, and then sends a message back to its master with a new SharedMemory*. - -// ScriptReloader is the worker that manages running the script scan -// on the file thread. -// It must be created on, and its public API must only be called from, -// the master's thread. -class GreasemonkeyMaster::ScriptReloader - : public base::RefCounted { - public: - ScriptReloader(GreasemonkeyMaster* master) - : master_(master), master_message_loop_(MessageLoop::current()) {} - - // Start a scan for scripts. - // Will always send a message to the master upon completion. - void StartScan(MessageLoop* work_loop, const FilePath& script_dir); - - // The master is going away; don't call it back. - void DisownMaster() { - master_ = NULL; - } - - private: - // Where functions are run: - // master file - // StartScan -> RunScan - // GetNewScripts() - // NotifyMaster <- RunScan - - // Runs on the master thread. - // Notify the master that new scripts are available. - void NotifyMaster(base::SharedMemory* memory); - - // Runs on the File thread. - // Scan the script directory for scripts, calling NotifyMaster when done. - // The path is intentionally passed by value so its lifetime isn't tied - // to the caller. - void RunScan(const FilePath script_dir); - - // Runs on the File thread. - // Scan the script directory for scripts, returning either a new SharedMemory - // or NULL on error. - base::SharedMemory* GetNewScripts(const FilePath& script_dir); - - // A pointer back to our master. - // May be NULL if DisownMaster() is called. - GreasemonkeyMaster* master_; - - // The message loop to call our master back on. - // Expected to always outlive us. - MessageLoop* master_message_loop_; - - DISALLOW_COPY_AND_ASSIGN(ScriptReloader); -}; - -void GreasemonkeyMaster::ScriptReloader::StartScan( - MessageLoop* work_loop, - const FilePath& script_dir) { - // Add a reference to ourselves to keep ourselves alive while we're running. - // Balanced by NotifyMaster(). - AddRef(); - work_loop->PostTask(FROM_HERE, - NewRunnableMethod(this, - &GreasemonkeyMaster::ScriptReloader::RunScan, - script_dir)); -} - -void GreasemonkeyMaster::ScriptReloader::NotifyMaster( - base::SharedMemory* memory) { - if (!master_) { - // The master went away, so these new scripts aren't useful anymore. - delete memory; - } else { - master_->NewScriptsAvailable(memory); - } - - // Drop our self-reference. - // Balances StartScan(). - Release(); -} - -void GreasemonkeyMaster::ScriptReloader::RunScan(const FilePath script_dir) { - base::SharedMemory* shared_memory = GetNewScripts(script_dir); - - // Post the new scripts back to the master's message loop. - master_message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, - &GreasemonkeyMaster::ScriptReloader::NotifyMaster, - shared_memory)); -} - -base::SharedMemory* GreasemonkeyMaster::ScriptReloader::GetNewScripts( - const FilePath& script_dir) { - std::vector scripts; - - file_util::FileEnumerator enumerator(script_dir, false, - file_util::FileEnumerator::FILES, - FILE_PATH_LITERAL("*.user.js")); - for (FilePath file = enumerator.Next(); !file.value().empty(); - file = enumerator.Next()) { - scripts.push_back(file.ToWStringHack()); - } - - if (scripts.empty()) - return NULL; - - // Pickle scripts data. - Pickle pickle; - pickle.WriteSize(scripts.size()); - for (std::vector::iterator path = scripts.begin(); - path != scripts.end(); ++path) { - std::string file_url = net::FilePathToFileURL(*path).spec(); - std::string contents; - // TODO(aa): Support unicode script files. - file_util::ReadFileToString(*path, &contents); - - // Write scripts as 'data' so that we can read it out in the slave without - // allocating a new string. - pickle.WriteData(file_url.c_str(), file_url.length()); - pickle.WriteData(contents.c_str(), contents.length()); - } - - // Create the shared memory object. - scoped_ptr shared_memory(new base::SharedMemory()); - - if (!shared_memory->Create(std::wstring(), // anonymous - false, // read-only - false, // open existing - pickle.size())) { - return NULL; - } - - // Map into our process. - if (!shared_memory->Map(pickle.size())) - return NULL; - - // Copy the pickle to shared memory. - memcpy(shared_memory->memory(), pickle.data(), pickle.size()); - - return shared_memory.release(); -} - - -GreasemonkeyMaster::GreasemonkeyMaster(MessageLoop* worker_loop, - const FilePath& script_dir) - : user_script_dir_(new FilePath(script_dir)), - dir_watcher_(new DirectoryWatcher), - worker_loop_(worker_loop), - pending_scan_(false) { - // Watch our scripts directory for modifications. - if (dir_watcher_->Watch(script_dir, this)) { - // (Asynchronously) scan for our initial set of scripts. - StartScan(); - } -} - -GreasemonkeyMaster::~GreasemonkeyMaster() { - if (script_reloader_) - script_reloader_->DisownMaster(); -} - -void GreasemonkeyMaster::NewScriptsAvailable(base::SharedMemory* handle) { - // Ensure handle is deleted or released. - scoped_ptr handle_deleter(handle); - - if (pending_scan_) { - // While we were scanning, there were further changes. Don't bother - // notifying about these scripts and instead just immediately rescan. - pending_scan_ = false; - StartScan(); - } else { - // We're no longer scanning. - script_reloader_ = NULL; - // We've got scripts ready to go. - shared_memory_.swap(handle_deleter); - - NotificationService::current()->Notify(NOTIFY_GREASEMONKEY_SCRIPTS_LOADED, - NotificationService::AllSources(), - Details(handle)); - } -} - -void GreasemonkeyMaster::OnDirectoryChanged(const FilePath& path) { - if (script_reloader_.get()) { - // We're already scanning for scripts. We note that we should rescan when - // we get the chance. - pending_scan_ = true; - return; - } - - StartScan(); -} - -void GreasemonkeyMaster::StartScan() { - if (!script_reloader_) - script_reloader_ = new ScriptReloader(this); - - script_reloader_->StartScan(worker_loop_, *user_script_dir_); -} diff --git a/chrome/browser/greasemonkey_master.h b/chrome/browser/greasemonkey_master.h deleted file mode 100644 index 34121dd..0000000 --- a/chrome/browser/greasemonkey_master.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_GREASEMONKEY_MASTER_H_ -#define CHROME_BROWSER_GREASEMONKEY_MASTER_H_ - -#include "base/directory_watcher.h" -#include "base/file_path.h" -#include "base/process.h" -#include "base/scoped_ptr.h" -#include "base/shared_memory.h" - -class MessageLoop; - -// Manages a segment of shared memory that contains the Greasemonkey scripts the -// user has installed. Lives on the UI thread. -class GreasemonkeyMaster : public base::RefCounted, - public DirectoryWatcher::Delegate { - public: - // For testability, the constructor takes the MessageLoop to run the - // script-reloading worker on as well as the path the scripts live in. - // These are normally the file thread and a directory inside the profile. - GreasemonkeyMaster(MessageLoop* worker, const FilePath& script_dir); - ~GreasemonkeyMaster(); - - // Gets the segment of shared memory for the scripts. - base::SharedMemory* GetSharedMemory() const { - return shared_memory_.get(); - } - - // Called by the script reloader when new scripts have been loaded. - void NewScriptsAvailable(base::SharedMemory* handle); - - // Return true if we have any scripts ready. - bool ScriptsReady() const { return shared_memory_.get() != NULL; } - - // Returns the path to the directory user scripts are stored in. - FilePath user_script_dir() const { return *user_script_dir_; } - - private: - class ScriptReloader; - - // DirectoryWatcher::Delegate implementation. - virtual void OnDirectoryChanged(const FilePath& path); - - // Kicks off a process on the file thread to reload scripts from disk - // into a new chunk of shared memory and notify renderers. - void StartScan(); - - // The directory containing user scripts. - scoped_ptr user_script_dir_; - - // The watcher watches the profile's user scripts directory for new scripts. - scoped_ptr dir_watcher_; - - // The MessageLoop that the scanner worker runs on. - // Typically the file thread; configurable for testing. - MessageLoop* worker_loop_; - - // ScriptReloader (in another thread) reloads script off disk. - // We hang on to our pointer to know if we've already got one running. - scoped_refptr script_reloader_; - - // Contains the scripts that were found the last time scripts were updated. - scoped_ptr shared_memory_; - - // If the script directory is modified while we're rescanning it, we note - // that we're currently mid-scan and then start over again once the scan - // finishes. This boolean tracks whether another scan is pending. - bool pending_scan_; - - DISALLOW_COPY_AND_ASSIGN(GreasemonkeyMaster); -}; - -#endif // CHROME_BROWSER_GREASEMONKEY_MASTER_H_ diff --git a/chrome/browser/greasemonkey_master_unittest.cc b/chrome/browser/greasemonkey_master_unittest.cc deleted file mode 100644 index 4ce63a0..0000000 --- a/chrome/browser/greasemonkey_master_unittest.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/greasemonkey_master.h" - -#include - -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/message_loop.h" -#include "base/path_service.h" -#include "base/string_util.h" -#include "chrome/common/notification_service.h" -#include "testing/gtest/include/gtest/gtest.h" - -// Test bringing up a master on a specific directory, putting a script in there, etc. - -class GreasemonkeyMasterTest : public testing::Test, - public NotificationObserver { - public: - GreasemonkeyMasterTest() : shared_memory_(NULL) {} - - virtual void SetUp() { - // Name a subdirectory of the temp directory. - std::wstring path_str; - ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &path_str)); - script_dir_ = FilePath(path_str).Append( - FILE_PATH_LITERAL("GreasemonkeyTest")); - - // Create a fresh, empty copy of this directory. - file_util::Delete(script_dir_.value(), true); - file_util::CreateDirectory(script_dir_.value()); - - // Register for all user script notifications. - NotificationService::current()->AddObserver(this, - NOTIFY_GREASEMONKEY_SCRIPTS_LOADED, - NotificationService::AllSources()); - } - - virtual void TearDown() { - NotificationService::current()->RemoveObserver(this, - NOTIFY_GREASEMONKEY_SCRIPTS_LOADED, - NotificationService::AllSources()); - - // Clean up test directory. - ASSERT_TRUE(file_util::Delete(script_dir_.value(), true)); - ASSERT_FALSE(file_util::PathExists(script_dir_.value())); - } - - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK(type == NOTIFY_GREASEMONKEY_SCRIPTS_LOADED); - - shared_memory_ = Details(details).ptr(); - if (MessageLoop::current() == &message_loop_) - MessageLoop::current()->Quit(); - } - - // MessageLoop used in tests. - MessageLoop message_loop_; - - // Directory containing user scripts. - FilePath script_dir_; - - // Updated to the script shared memory when we get notified. - base::SharedMemory* shared_memory_; -}; - -// Test that we *don't* get spurious notifications. -TEST_F(GreasemonkeyMasterTest, NoScripts) { - // Set shared_memory_ to something non-NULL, so we can check it became NULL. - shared_memory_ = reinterpret_cast(1); - - scoped_refptr master( - new GreasemonkeyMaster(MessageLoop::current(), script_dir_)); - message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); - message_loop_.Run(); - - // There were no scripts in the script dir, so we shouldn't have gotten - // a notification. - ASSERT_EQ(NULL, shared_memory_); -} - -// Test that we get notified about new scripts after they're added. -TEST_F(GreasemonkeyMasterTest, NewScripts) { - scoped_refptr master( - new GreasemonkeyMaster(MessageLoop::current(), script_dir_)); - - FilePath path = script_dir_.Append(FILE_PATH_LITERAL("script.user.js")); - - std::ofstream file; - file.open(WideToUTF8(path.value()).c_str()); - file << "some content"; - file.close(); - - message_loop_.Run(); - - ASSERT_TRUE(shared_memory_ != NULL); -} - -// Test that we get notified about scripts if they're already in the test dir. -TEST_F(GreasemonkeyMasterTest, ExistingScripts) { - FilePath path = script_dir_.Append(FILE_PATH_LITERAL("script.user.js")); - std::ofstream file; - file.open(WideToUTF8(path.value()).c_str()); - file << "some content"; - file.close(); - - scoped_refptr master( - new GreasemonkeyMaster(MessageLoop::current(), script_dir_)); - - message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); - message_loop_.Run(); - - ASSERT_TRUE(shared_memory_ != NULL); -} diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc index 2603b296..5ba3f63 100644 --- a/chrome/browser/net/chrome_url_request_context.cc +++ b/chrome/browser/net/chrome_url_request_context.cc @@ -9,7 +9,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/extensions/extensions_service.h" -#include "chrome/browser/greasemonkey_master.h" +#include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/profile.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" @@ -112,7 +112,7 @@ ChromeURLRequestContext::ChromeURLRequestContext(Profile* profile) extension_paths_[(*iter)->id()] = (*iter)->path(); } - user_script_dir_path_ = profile->GetGreasemonkeyMaster()->user_script_dir(); + user_script_dir_path_ = profile->GetUserScriptMaster()->user_script_dir(); prefs_->AddPrefObserver(prefs::kAcceptLanguages, this); prefs_->AddPrefObserver(prefs::kCookieBehavior, this); diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 6168275..b1762f0 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -18,7 +18,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_manager.h" #include "chrome/browser/extensions/extensions_service.h" -#include "chrome/browser/greasemonkey_master.h" +#include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/history/history.h" #include "chrome/browser/navigation_controller.h" #include "chrome/browser/net/chrome_url_request_context.h" @@ -134,8 +134,8 @@ class OffTheRecordProfileImpl : public Profile, return profile_->GetExtensionsService(); } - virtual GreasemonkeyMaster* GetGreasemonkeyMaster() { - return profile_->GetGreasemonkeyMaster(); + virtual UserScriptMaster* GetUserScriptMaster() { + return profile_->GetUserScriptMaster(); } virtual HistoryService* GetHistoryService(ServiceAccessType sat) { @@ -448,16 +448,16 @@ ExtensionsService* ProfileImpl::GetExtensionsService() { return extensions_service_.get(); } -GreasemonkeyMaster* ProfileImpl::GetGreasemonkeyMaster() { - if (!greasemonkey_master_.get()) { +UserScriptMaster* ProfileImpl::GetUserScriptMaster() { + if (!user_script_master_.get()) { std::wstring script_dir = GetPath(); file_util::AppendToPath(&script_dir, chrome::kUserScriptsDirname); - greasemonkey_master_ = - new GreasemonkeyMaster(g_browser_process->file_thread()->message_loop(), - FilePath(script_dir)); + user_script_master_ = + new UserScriptMaster(g_browser_process->file_thread()->message_loop(), + FilePath(script_dir)); } - return greasemonkey_master_.get(); + return user_script_master_.get(); } PrefService* ProfileImpl::GetPrefs() { diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h index 16455c1..f5e0866 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -26,7 +26,6 @@ class BookmarkModel; class ChromeURLRequestContext; class DownloadManager; class ExtensionsService; -class GreasemonkeyMaster; class HistoryService; class NavigationController; class PrefService; @@ -36,6 +35,7 @@ class TabRestoreService; class TemplateURLFetcher; class TemplateURLModel; class URLRequestContext; +class UserScriptMaster; class VisitedLinkMaster; class WebDataService; @@ -109,10 +109,10 @@ class Profile { // profile. The ExtensionsService is created at startup. virtual ExtensionsService* GetExtensionsService() = 0; - // Retrieves a pointer to the GreasemonkeyMaster associated with this - // profile. The GreasemonkeyMaster is lazily created the first time + // Retrieves a pointer to the UserScriptMaster associated with this + // profile. The UserScriptMaster is lazily created the first time // that this method is called. - virtual GreasemonkeyMaster* GetGreasemonkeyMaster() = 0; + virtual UserScriptMaster* GetUserScriptMaster() = 0; // Retrieves a pointer to the HistoryService associated with this // profile. The HistoryService is lazily created the first time @@ -260,7 +260,7 @@ class ProfileImpl : public Profile, virtual Profile* GetOffTheRecordProfile(); virtual Profile* GetOriginalProfile(); virtual VisitedLinkMaster* GetVisitedLinkMaster(); - virtual GreasemonkeyMaster* GetGreasemonkeyMaster(); + virtual UserScriptMaster* GetUserScriptMaster(); virtual ExtensionsService* GetExtensionsService(); virtual HistoryService* GetHistoryService(ServiceAccessType sat); virtual WebDataService* GetWebDataService(ServiceAccessType sat); @@ -321,7 +321,7 @@ class ProfileImpl : public Profile, bool off_the_record_; scoped_ptr visited_link_master_; scoped_refptr extensions_service_; - scoped_refptr greasemonkey_master_; + scoped_refptr user_script_master_; scoped_ptr prefs_; scoped_ptr template_url_fetcher_; scoped_ptr template_url_model_; diff --git a/chrome/browser/render_process_host.cc b/chrome/browser/render_process_host.cc index 1efcd58d..f3a2e88 100644 --- a/chrome/browser/render_process_host.cc +++ b/chrome/browser/render_process_host.cc @@ -30,6 +30,7 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/cache_manager_host.h" +#include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/history/history.h" #include "chrome/browser/plugin_service.h" #include "chrome/browser/render_widget_helper.h" @@ -39,7 +40,6 @@ #include "chrome/browser/sandbox_policy.h" #include "chrome/browser/spellchecker.h" #include "chrome/browser/visitedlink_master.h" -#include "chrome/browser/greasemonkey_master.h" #include "chrome/browser/web_contents.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" @@ -167,8 +167,7 @@ RenderProcessHost::RenderProcessHost(Profile* profile) profile->GetPrefs()->GetBoolean(prefs::kBlockPopups)); NotificationService::current()->AddObserver(this, - NOTIFY_GREASEMONKEY_SCRIPTS_LOADED, - NotificationService::AllSources()); + NOTIFY_USER_SCRIPTS_LOADED, NotificationService::AllSources()); // Note: When we create the RenderProcessHost, it's technically backgrounded, // because it has no visible listeners. But the process doesn't @@ -191,8 +190,7 @@ RenderProcessHost::~RenderProcessHost() { profile_->GetPrefs()->RemovePrefObserver(prefs::kBlockPopups, this); NotificationService::current()->RemoveObserver(this, - NOTIFY_GREASEMONKEY_SCRIPTS_LOADED, - NotificationService::AllSources()); + NOTIFY_USER_SCRIPTS_LOADED, NotificationService::AllSources()); } void RenderProcessHost::Unregister() { @@ -278,7 +276,7 @@ bool RenderProcessHost::Init() { switches::kDisablePopupBlocking, switches::kUseLowFragHeapCrt, switches::kGearsInRenderer, - switches::kEnableGreasemonkey, + switches::kEnableUserScripts, switches::kEnableVideo, }; @@ -436,7 +434,7 @@ bool RenderProcessHost::Init() { SetBackgrounded(backgrounded_); InitVisitedLinks(); - InitGreasemonkeyScripts(); + InitUserScripts(); if (max_page_id_ != -1) channel_->Send(new ViewMsg_SetNextPageID(max_page_id_ + 1)); @@ -465,9 +463,9 @@ void RenderProcessHost::InitVisitedLinks() { } } -void RenderProcessHost::InitGreasemonkeyScripts() { +void RenderProcessHost::InitUserScripts() { CommandLine command_line; - if (!command_line.HasSwitch(switches::kEnableGreasemonkey)) { + if (!command_line.HasSwitch(switches::kEnableUserScripts)) { return; } @@ -477,28 +475,28 @@ void RenderProcessHost::InitGreasemonkeyScripts() { // - File IO should be asynchronous (see VisitedLinkMaster), but how do we // get scripts to the first renderer without blocking startup? Should we // cache some information across restarts? - GreasemonkeyMaster* greasemonkey_master = profile_->GetGreasemonkeyMaster(); - if (!greasemonkey_master) { + UserScriptMaster* user_script_master = profile_->GetUserScriptMaster(); + if (!user_script_master) { return; } - if (!greasemonkey_master->ScriptsReady()) { + if (!user_script_master->ScriptsReady()) { // No scripts ready. :( return; } // Update the renderer process with the current scripts. - SendGreasemonkeyScriptsUpdate(greasemonkey_master->GetSharedMemory()); + SendUserScriptsUpdate(user_script_master->GetSharedMemory()); } -void RenderProcessHost::SendGreasemonkeyScriptsUpdate( +void RenderProcessHost::SendUserScriptsUpdate( base::SharedMemory *shared_memory) { base::SharedMemoryHandle handle_for_process = NULL; shared_memory->ShareToProcess(GetRendererProcessHandle(), &handle_for_process); DCHECK(handle_for_process); if (handle_for_process) { - channel_->Send(new ViewMsg_Greasemonkey_NewScripts(handle_for_process)); + channel_->Send(new ViewMsg_UserScripts_NewScripts(handle_for_process)); } } @@ -819,12 +817,12 @@ void RenderProcessHost::Observe(NotificationType type, } break; } - case NOTIFY_GREASEMONKEY_SCRIPTS_LOADED: { + case NOTIFY_USER_SCRIPTS_LOADED: { base::SharedMemory* shared_memory = Details(details).ptr(); DCHECK(shared_memory); if (shared_memory) { - SendGreasemonkeyScriptsUpdate(shared_memory); + SendUserScriptsUpdate(shared_memory); } break; } diff --git a/chrome/browser/render_process_host.h b/chrome/browser/render_process_host.h index 597cc2b3..e4fea50 100644 --- a/chrome/browser/render_process_host.h +++ b/chrome/browser/render_process_host.h @@ -205,12 +205,12 @@ class RenderProcessHost : public IPC::Channel::Listener, // set of visited links. void InitVisitedLinks(); - // Initialize support for Greasemonkey scripts. Send the renderer process its - // initial set of scripts and listen for updates to scripts. - void InitGreasemonkeyScripts(); + // Initialize support for user scripts. Send the renderer process its initial + // set of scripts and listen for updates to scripts. + void InitUserScripts(); - // Sends the renderer process a new set of Greasemonkey scripts. - void SendGreasemonkeyScriptsUpdate(base::SharedMemory* shared_memory); + // Sends the renderer process a new set of user scripts. + void SendUserScriptsUpdate(base::SharedMemory* shared_memory); // Gets a handle to the renderer process, normalizing the case where we were // started with --single-process. diff --git a/chrome/chrome.xcodeproj/project.pbxproj b/chrome/chrome.xcodeproj/project.pbxproj index b8325b8..8492784 100644 --- a/chrome/chrome.xcodeproj/project.pbxproj +++ b/chrome/chrome.xcodeproj/project.pbxproj @@ -74,6 +74,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 0082A7540F16987A000AA0EF /* user_script_master.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0082A7510F16987A000AA0EF /* user_script_master.cc */; }; 4D640CF30EAE86E300EBCFC0 /* about_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D640CBD0EAE868600EBCFC0 /* about_handler.cc */; }; 4D640CF40EAE86EA00EBCFC0 /* render_dns_queue.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D640CE40EAE86A500EBCFC0 /* render_dns_queue.cc */; }; 4D640CF50EAE86EF00EBCFC0 /* visitedlink_slave.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D640CDC0EAE868600EBCFC0 /* visitedlink_slave.cc */; }; @@ -189,8 +190,8 @@ A54612E20EE995F600A8EE5D /* extensions_service.cc in Sources */ = {isa = PBXBuildFile; fileRef = A54612D90EE9957000A8EE5D /* extensions_service.cc */; }; B502DA280F098056005BE90C /* visit_database_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFA180E9D48F7009A6919 /* visit_database_unittest.cc */; }; B502DA520F098888005BE90C /* l10n_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFBC90E9D4C9F009A6919 /* l10n_util.cc */; }; - B503E0F00F0175FD00547DC6 /* greasemonkey_slave.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D640CC90EAE868600EBCFC0 /* greasemonkey_slave.cc */; }; - B503E0FC0F01764800547DC6 /* greasemonkey_slave_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = B503E0FB0F01764800547DC6 /* greasemonkey_slave_unittest.cc */; }; + B503E0F00F0175FD00547DC6 /* user_script_slave.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D640CC90EAE868600EBCFC0 /* user_script_slave.cc */; }; + B503E0FC0F01764800547DC6 /* user_script_slave_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = B503E0FB0F01764800547DC6 /* user_script_slave_unittest.cc */; }; B503E1030F017C1000547DC6 /* librenderer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D640CEB0EAE86BD00EBCFC0 /* librenderer.a */; }; B507AC1F0F0048E10060FEE8 /* ipc_sync_message.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFBBA0E9D4C9F009A6919 /* ipc_sync_message.cc */; }; B507AC440F004B610060FEE8 /* ipc_sync_message_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BFBBC0E9D4C9F009A6919 /* ipc_sync_message_unittest.cc */; }; @@ -241,7 +242,6 @@ E45075B40F1505C9003BE099 /* bookmark_html_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = E45075B30F1505C9003BE099 /* bookmark_html_writer.cc */; }; E45075B70F1505DD003BE099 /* keyword_provider.cc in Sources */ = {isa = PBXBuildFile; fileRef = E45075AA0F150564003BE099 /* keyword_provider.cc */; }; E45075B80F15060C003BE099 /* browser_trial.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D640D120EAE87BD00EBCFC0 /* browser_trial.cc */; }; - E45075BA0F15064A003BE099 /* greasemonkey_master.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D640D140EAE87BD00EBCFC0 /* greasemonkey_master.cc */; }; E45075BC0F1506B5003BE099 /* history_model.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D7BF88C0E9D4839009A6919 /* history_model.cc */; }; E45075C10F1506F2003BE099 /* firefox2_importer.cc in Sources */ = {isa = PBXBuildFile; fileRef = E45075C00F1506F2003BE099 /* firefox2_importer.cc */; }; E45075C40F150701003BE099 /* firefox3_importer.cc in Sources */ = {isa = PBXBuildFile; fileRef = E45075C30F150701003BE099 /* firefox3_importer.cc */; }; @@ -889,6 +889,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0082A7510F16987A000AA0EF /* user_script_master.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_script_master.cc; sourceTree = ""; }; + 0082A7520F16987A000AA0EF /* user_script_master.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = user_script_master.h; sourceTree = ""; }; 4D640CBD0EAE868600EBCFC0 /* about_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = about_handler.cc; sourceTree = ""; }; 4D640CBE0EAE868600EBCFC0 /* about_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = about_handler.h; sourceTree = ""; }; 4D640CBF0EAE868600EBCFC0 /* chrome_plugin_host.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chrome_plugin_host.cc; sourceTree = ""; }; @@ -901,8 +903,8 @@ 4D640CC60EAE868600EBCFC0 /* external_host_bindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = external_host_bindings.h; sourceTree = ""; }; 4D640CC70EAE868600EBCFC0 /* external_js_object.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = external_js_object.cc; sourceTree = ""; }; 4D640CC80EAE868600EBCFC0 /* external_js_object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = external_js_object.h; sourceTree = ""; }; - 4D640CC90EAE868600EBCFC0 /* greasemonkey_slave.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = greasemonkey_slave.cc; sourceTree = ""; }; - 4D640CCA0EAE868600EBCFC0 /* greasemonkey_slave.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = greasemonkey_slave.h; sourceTree = ""; }; + 4D640CC90EAE868600EBCFC0 /* user_script_slave.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_script_slave.cc; sourceTree = ""; }; + 4D640CCA0EAE868600EBCFC0 /* user_script_slave.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = user_script_slave.h; sourceTree = ""; }; 4D640CCB0EAE868600EBCFC0 /* localized_error.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = localized_error.cc; sourceTree = ""; }; 4D640CCC0EAE868600EBCFC0 /* localized_error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = localized_error.h; sourceTree = ""; }; 4D640CCD0EAE868600EBCFC0 /* plugin_channel_host.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = plugin_channel_host.cc; sourceTree = ""; }; @@ -932,8 +934,6 @@ 4D640CEB0EAE86BD00EBCFC0 /* librenderer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = librenderer.a; sourceTree = BUILT_PRODUCTS_DIR; }; 4D640D120EAE87BD00EBCFC0 /* browser_trial.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = browser_trial.cc; sourceTree = ""; }; 4D640D130EAE87BD00EBCFC0 /* browser_trial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = browser_trial.h; sourceTree = ""; }; - 4D640D140EAE87BD00EBCFC0 /* greasemonkey_master.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = greasemonkey_master.cc; sourceTree = ""; }; - 4D640D150EAE87BD00EBCFC0 /* greasemonkey_master.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = greasemonkey_master.h; sourceTree = ""; }; 4D640D160EAE87BD00EBCFC0 /* interstitial_page.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = interstitial_page.cc; sourceTree = ""; }; 4D640D170EAE87BD00EBCFC0 /* interstitial_page.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interstitial_page.h; sourceTree = ""; }; 4D640D180EAE87BD00EBCFC0 /* render_widget_host_view_win.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = render_widget_host_view_win.cc; sourceTree = ""; }; @@ -1585,7 +1585,7 @@ A54612D90EE9957000A8EE5D /* extensions_service.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = extensions_service.cc; sourceTree = ""; }; A54612DA0EE9957000A8EE5D /* extensions_service.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = extensions_service.h; sourceTree = ""; }; A54612DB0EE9958600A8EE5D /* extensions_service_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = extensions_service_unittest.cc; sourceTree = ""; }; - B503E0FB0F01764800547DC6 /* greasemonkey_slave_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = greasemonkey_slave_unittest.cc; sourceTree = ""; }; + B503E0FB0F01764800547DC6 /* user_script_slave_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_script_slave_unittest.cc; sourceTree = ""; }; B54BD8FA0ED622C00093FD54 /* mach_message_source_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mach_message_source_mac.cc; sourceTree = ""; }; B54BD8FB0ED622C00093FD54 /* mach_message_source_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_message_source_mac.h; sourceTree = ""; }; B562C8410ED49C830077A23F /* mach_ipc_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_ipc_mac.h; sourceTree = ""; }; @@ -1803,9 +1803,6 @@ 4D640CC60EAE868600EBCFC0 /* external_host_bindings.h */, 4D640CC70EAE868600EBCFC0 /* external_js_object.cc */, 4D640CC80EAE868600EBCFC0 /* external_js_object.h */, - 4D640CC90EAE868600EBCFC0 /* greasemonkey_slave.cc */, - 4D640CCA0EAE868600EBCFC0 /* greasemonkey_slave.h */, - B503E0FB0F01764800547DC6 /* greasemonkey_slave_unittest.cc */, 4D640CCB0EAE868600EBCFC0 /* localized_error.cc */, 4D640CCC0EAE868600EBCFC0 /* localized_error.h */, 4D640CCD0EAE868600EBCFC0 /* plugin_channel_host.cc */, @@ -1822,6 +1819,9 @@ 4D640CD80EAE868600EBCFC0 /* renderer_glue.cc */, 4D640CD90EAE868600EBCFC0 /* renderer_main.cc */, 4D640CDA0EAE868600EBCFC0 /* renderer_resources.h */, + 4D640CC90EAE868600EBCFC0 /* user_script_slave.cc */, + 4D640CCA0EAE868600EBCFC0 /* user_script_slave.h */, + B503E0FB0F01764800547DC6 /* user_script_slave_unittest.cc */, 4D640CDC0EAE868600EBCFC0 /* visitedlink_slave.cc */, 4D640CDD0EAE868600EBCFC0 /* visitedlink_slave.h */, 4D640CDE0EAE868600EBCFC0 /* webplugin_delegate_proxy.cc */, @@ -2038,8 +2038,6 @@ 4D7BF8890E9D4839009A6919 /* google_url_tracker_unittest.cc */, 4D7BF88A0E9D4839009A6919 /* google_util.cc */, 4D7BF88B0E9D4839009A6919 /* google_util.h */, - 4D640D140EAE87BD00EBCFC0 /* greasemonkey_master.cc */, - 4D640D150EAE87BD00EBCFC0 /* greasemonkey_master.h */, 4D7BF88C0E9D4839009A6919 /* history_model.cc */, 4D7BF88D0E9D4839009A6919 /* history_model.h */, 4D7BF88E0E9D4839009A6919 /* history_tab_ui.cc */, @@ -2936,6 +2934,8 @@ E4F3243F0EE5CE94002533CE /* extensions */ = { isa = PBXGroup; children = ( + 0082A7510F16987A000AA0EF /* user_script_master.cc */, + 0082A7520F16987A000AA0EF /* user_script_master.h */, E4F324400EE5CE94002533CE /* extension.cc */, E4F324410EE5CE94002533CE /* extension.h */, E4F324420EE5CE94002533CE /* extension_unittest.cc */, @@ -3516,7 +3516,7 @@ E45076D90F1538E4003BE099 /* dom_automation_controller.cc in Sources */, E45076C90F1537F5003BE099 /* dom_ui_bindings.cc in Sources */, E45076CB0F15380C003BE099 /* external_host_bindings.cc in Sources */, - B503E0F00F0175FD00547DC6 /* greasemonkey_slave.cc in Sources */, + B503E0F00F0175FD00547DC6 /* user_script_slave.cc in Sources */, E45076CF0F153871003BE099 /* localized_error.cc in Sources */, 4D640CF40EAE86EA00EBCFC0 /* render_dns_queue.cc in Sources */, 4D640CF50EAE86EF00EBCFC0 /* visitedlink_slave.cc in Sources */, @@ -3557,7 +3557,6 @@ E45075C60F15070D003BE099 /* firefox_profile_lock.cc in Sources */, 4D7BF9990E9D486B009A6919 /* google_url_tracker.cc in Sources */, 4D7BF99E0E9D486F009A6919 /* google_util.cc in Sources */, - E45075BA0F15064A003BE099 /* greasemonkey_master.cc in Sources */, E4F3244A0EE5CF3C002533CE /* history_backend.cc in Sources */, E4F3244D0EE5CF4A002533CE /* history_database.cc in Sources */, E45075BC0F1506B5003BE099 /* history_model.cc in Sources */, @@ -3629,7 +3628,7 @@ 4D7BFB510E9D4C3E009A6919 /* dns_host_info_unittest.cc in Sources */, E4F324950EE5D758002533CE /* extension_unittest.cc in Sources */, A54612DC0EE9958600A8EE5D /* extensions_service_unittest.cc in Sources */, - B503E0FC0F01764800547DC6 /* greasemonkey_slave_unittest.cc in Sources */, + B503E0FC0F01764800547DC6 /* user_script_slave_unittest.cc in Sources */, 4D7BFB3C0E9D4C25009A6919 /* history_types_unittest.cc in Sources */, B507AC440F004B610060FEE8 /* ipc_sync_message_unittest.cc in Sources */, 4D7BFCE30E9D4DCE009A6919 /* jpeg_codec_unittest.cc in Sources */, @@ -3650,6 +3649,7 @@ 4D7BFB610E9D4C4B009A6919 /* units_unittest.cc in Sources */, B502DA280F098056005BE90C /* visit_database_unittest.cc in Sources */, 4D7BFB420E9D4C35009A6919 /* visit_tracker_unittest.cc in Sources */, + 0082A7540F16987A000AA0EF /* user_script_master.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index b2c1937..cdf8e3c 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -333,8 +333,8 @@ const wchar_t kEnableP13n[] = L"enable-p13n"; // SDCH is currently only supported server-side for searches on google.com. const wchar_t kSdchFilter[] = L"enable-sdch"; -// Enable Greasemonkey script support. -const wchar_t kEnableGreasemonkey[] = L"enable-user-scripts"; +// Enable user script support. +const wchar_t kEnableUserScripts[] = L"enable-user-scripts"; // Enable extensions. const wchar_t kEnableExtensions[] = L"enable-extensions"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 78e8212..fe9478b 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -129,7 +129,7 @@ extern const wchar_t kEnableP13n[]; extern const wchar_t kSdchFilter[]; -extern const wchar_t kEnableGreasemonkey[]; +extern const wchar_t kEnableUserScripts[]; extern const wchar_t kEnableExtensions[]; extern const wchar_t kIncognito[]; diff --git a/chrome/common/notification_types.h b/chrome/common/notification_types.h index be77915..c2a0c09 100644 --- a/chrome/common/notification_types.h +++ b/chrome/common/notification_types.h @@ -495,11 +495,11 @@ enum NotificationType { // Personalization ----------------------------------------------------------- NOTIFY_PERSONALIZATION, - // Greasemonkey user scripts ------------------------------------------------- + // User Scripts -------------------------------------------------------------- // Sent when there are new user scripts available. // The details are a pointer to SharedMemory containing the new scripts. - NOTIFY_GREASEMONKEY_SCRIPTS_LOADED, + NOTIFY_USER_SCRIPTS_LOADED, // Extensions ---------------------------------------------------------------- diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 44aa3cc..4160da8 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -165,10 +165,10 @@ IPC_BEGIN_MESSAGES(View, 1) // handle. This handle is valid in the context of the renderer IPC_MESSAGE_CONTROL1(ViewMsg_VisitedLink_NewTable, base::SharedMemoryHandle) - // Notification that the Greasemonkey scripts have been updated. It has one + // Notification that the user scripts have been updated. It has one // SharedMemoryHandle argument consisting of the pickled script data. This // handle is valid in the context of the renderer. - IPC_MESSAGE_CONTROL1(ViewMsg_Greasemonkey_NewScripts, base::SharedMemoryHandle) + IPC_MESSAGE_CONTROL1(ViewMsg_UserScripts_NewScripts, base::SharedMemoryHandle) // Sent when the user wants to search for a word on the page (find in page). // Request parameters are passed in as a FindInPageMsg_Request struct. diff --git a/chrome/renderer/greasemonkey_slave.cc b/chrome/renderer/greasemonkey_slave.cc deleted file mode 100644 index 4cf9439..0000000 --- a/chrome/renderer/greasemonkey_slave.cc +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/renderer/greasemonkey_slave.h" - -#include "base/logging.h" -#include "base/pickle.h" -#include "base/shared_memory.h" -#include "googleurl/src/gurl.h" - - -// GreasemonkeyScript - -void GreasemonkeyScript::Parse(const StringPiece& script_text) { - ParseMetadata(script_text); - - // TODO(aa): Set body to just the part after the metadata block? This would - // significantly cut down on the size of the injected script in some cases. - // Would require remembering the line number the body begins at, for correct - // error line number reporting. - body_ = script_text; -} - -bool GreasemonkeyScript::MatchesUrl(const GURL& url) { - for (std::vector::iterator pattern = include_patterns_.begin(); - pattern != include_patterns_.end(); ++pattern) { - if (MatchPattern(url.spec(), *pattern)) { - return true; - } - } - - return false; -} - -void GreasemonkeyScript::ParseMetadata(const StringPiece& script_text) { - // http://wiki.greasespot.net/Metadata_block - StringPiece line; - size_t line_start = 0; - size_t line_end = 0; - bool in_metadata = false; - - static const StringPiece kUserScriptBegin("// ==UserScript=="); - static const StringPiece kUserScriptEng("// ==/UserScript=="); - static const StringPiece kIncludeDeclaration("// @include "); - - while (line_start < script_text.length()) { - line_end = script_text.find('\n', line_start); - - // Handle the case where there is no trailing newline in the file. - if (line_end == std::string::npos) { - line_end = script_text.length() - 1; - } - - line.set(script_text.data() + line_start, line_end - line_start); - - if (!in_metadata) { - if (line.starts_with(kUserScriptBegin)) { - in_metadata = true; - } - } else { - if (line.starts_with(kUserScriptEng)) { - break; - } - - if (line.starts_with(kIncludeDeclaration)) { - std::string pattern(line.data() + kIncludeDeclaration.length(), - line.length() - kIncludeDeclaration.length()); - std::string pattern_trimmed; - TrimWhitespace(pattern, TRIM_ALL, &pattern_trimmed); - AddInclude(pattern_trimmed); - } - - // TODO(aa): Handle more types of metadata. - } - - line_start = line_end + 1; - } - - // If no @include patterns were specified, default to @include *. - // This is what Greasemonkey for Firefox does. - if (include_patterns_.size() == 0) { - AddInclude("*"); - } -} - -void GreasemonkeyScript::AddInclude(const std::string &glob_pattern) { - include_patterns_.push_back(EscapeGlob(glob_pattern)); -} - -std::string GreasemonkeyScript::EscapeGlob(const std::string& input_pattern) { - std::string output_pattern; - - for (size_t i = 0; i < input_pattern.length(); ++i) { - switch (input_pattern[i]) { - // These characters have special meaning to the MatchPattern() function, - // so we escape them. - case '\\': - case '?': - output_pattern += '\\'; - // fall through - - default: - output_pattern += input_pattern[i]; - } - } - - return output_pattern; -} - - -// GreasemonkeySlave -GreasemonkeySlave::GreasemonkeySlave() : shared_memory_(NULL) { -} - -bool GreasemonkeySlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { - scripts_.clear(); - - // Create the shared memory object (read only). - shared_memory_.reset(new base::SharedMemory(shared_memory, true)); - if (!shared_memory_.get()) - return false; - - // First get the size of the memory block. - if (!shared_memory_->Map(sizeof(Pickle::Header))) - return false; - Pickle::Header* pickle_header = - reinterpret_cast(shared_memory_->memory()); - - // Now map in the rest of the block. - int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size; - shared_memory_->Unmap(); - if (!shared_memory_->Map(pickle_size)) - return false; - - // Unpickle scripts. - void* iter = NULL; - int num_scripts = 0; - Pickle pickle(reinterpret_cast(shared_memory_->memory()), - pickle_size); - pickle.ReadInt(&iter, &num_scripts); - - for (int i = 0; i < num_scripts; ++i) { - const char* url = NULL; - int url_length = 0; - const char* body = NULL; - int body_length = 0; - - pickle.ReadData(&iter, &url, &url_length); - pickle.ReadData(&iter, &body, &body_length); - - scripts_.push_back(GreasemonkeyScript(StringPiece(url, url_length))); - GreasemonkeyScript& script = scripts_.back(); - script.Parse(StringPiece(body, body_length)); - } - - return true; -} - -bool GreasemonkeySlave::InjectScripts(WebFrame* frame) { - for (std::vector::iterator script = scripts_.begin(); - script != scripts_.end(); ++script) { - if (script->MatchesUrl(frame->GetURL())) { - frame->ExecuteJavaScript(script->GetBody().as_string(), - GURL(script->GetURL().as_string())); - } - } - - return true; -} diff --git a/chrome/renderer/greasemonkey_slave.h b/chrome/renderer/greasemonkey_slave.h deleted file mode 100644 index 5f80f8b..0000000 --- a/chrome/renderer/greasemonkey_slave.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_RENDERER_GREASEMONKEY_SLAVE_H_ -#define CHROME_RENDERER_GREASEMONKEY_SLAVE_H_ - -#include "base/scoped_ptr.h" -#include "base/shared_memory.h" -#include "base/string_piece.h" -#include "base/string_util.h" -#include "testing/gtest/include/gtest/gtest_prod.h" -#include "webkit/glue/webframe.h" - - -// Parsed representation of a Greasemonkey script. -class GreasemonkeyScript { - public: - GreasemonkeyScript(const StringPiece& script_url) - : url_(script_url) {} - - // Gets the script body that should be injected into matching content. - const StringPiece& GetBody() const { - return body_; - } - - // Gets a URL where this script can be found. - const StringPiece& GetURL() const { - return url_; - } - - // Parses the text content of a user script file. - void Parse(const StringPiece& script_text); - - // Returns true if the script should be applied to the specified URL, false - // otherwise. - bool MatchesUrl(const GURL& url); - - private: - FRIEND_TEST(GreasemonkeySlaveTest, EscapeGlob); - FRIEND_TEST(GreasemonkeySlaveTest, Parse1); - FRIEND_TEST(GreasemonkeySlaveTest, Parse2); - FRIEND_TEST(GreasemonkeySlaveTest, Parse3); - - // Helper function to convert the Greasemonkey glob format to the patterns - // used internally to test URLs. - static std::string EscapeGlob(const std::string& glob); - - // Parses the metadata block from the script. - void ParseMetadata(const StringPiece& script_text); - - // Adds an include pattern that will be checked to determine whether to - // include a script on a given page. - void AddInclude(const std::string &glob_pattern); - - // The body of the script, which will be injected into content pages. This - // references shared_memory_, and is valid until that memory is either - // deleted or Unmap()'d. - StringPiece body_; - - // The url of the file the script came from. This references shared_memory_, - // and is valid until that memory is either deleted or Unmap()'d. - StringPiece url_; - - // List of patterns to test URLs against for this script. These patterns have - // been escaped for use with MatchPattern() in string_utils ('?' and '\' are - // escaped). - std::vector include_patterns_; -}; - - -// Manages installed GreasemonkeyScripts for a render process. -class GreasemonkeySlave { - public: - GreasemonkeySlave(); - - // Update the parsed scripts from shared memory. - bool UpdateScripts(base::SharedMemoryHandle shared_memory); - - // Inject the appropriate scripts into a frame based on its URL. - // TODO(aa): Extract a GreasemonkeyFrame interface out of this to improve - // testability. - bool InjectScripts(WebFrame* frame); - - private: - // Shared memory containing raw script data. - scoped_ptr shared_memory_; - - // Parsed script data. - std::vector scripts_; - - DISALLOW_COPY_AND_ASSIGN(GreasemonkeySlave); -}; - -#endif // CHROME_RENDERER_GREASEMONKEY_SLAVE_H_ diff --git a/chrome/renderer/greasemonkey_slave_unittest.cc b/chrome/renderer/greasemonkey_slave_unittest.cc deleted file mode 100644 index 4d1f5ad..0000000 --- a/chrome/renderer/greasemonkey_slave_unittest.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/logging.h" -#include "chrome/renderer/greasemonkey_slave.h" -#include "googleurl/src/gurl.h" -#include "testing/gtest/include/gtest/gtest.h" - -TEST(GreasemonkeySlaveTest, EscapeGlob) { - EXPECT_EQ("", GreasemonkeyScript::EscapeGlob("")); - EXPECT_EQ("*", GreasemonkeyScript::EscapeGlob("*")); - EXPECT_EQ("www.google.com", GreasemonkeyScript::EscapeGlob("www.google.com")); - EXPECT_EQ("*google.com*", GreasemonkeyScript::EscapeGlob("*google.com*")); - EXPECT_EQ("foo\\\\bar\\?hot=dog", - GreasemonkeyScript::EscapeGlob("foo\\bar?hot=dog")); -} - -TEST(GreasemonkeySlaveTest, Parse1) { - const std::string text( - "// This is my awesome script\n" - "// It does stuff.\n" - "// ==UserScript== trailing garbage\n" - "// @name foobar script\n" - "// @namespace http://www.google.com/\n" - "// @include *mail.google.com*\n" - "// \n" - "// @othergarbage\n" - "// @include *mail.yahoo.com*\r\n" - "// @include \t *mail.msn.com*\n" // extra spaces after "@include" OK - "//@include not-recognized\n" // must have one space after "//" - "// ==/UserScript== trailing garbage\n" - "\n" - "\n" - "alert('hoo!');\n"); - - GreasemonkeyScript script("foo"); - script.Parse(text); - EXPECT_EQ(3U, script.include_patterns_.size()); - EXPECT_EQ(text, script.GetBody()); - EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.google.com"))); - EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.google.com/foo"))); - EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.yahoo.com/bar"))); - EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.msn.com/baz"))); - EXPECT_FALSE(script.MatchesUrl(GURL("http://www.hotmail.com"))); -} - -TEST(GreasemonkeySlaveTest, Parse2) { - const std::string text("default to @include *"); - - GreasemonkeyScript script("foo"); - script.Parse(text); - EXPECT_EQ(1U, script.include_patterns_.size()); - EXPECT_EQ(text, script.GetBody()); - EXPECT_TRUE(script.MatchesUrl(GURL("foo"))); - EXPECT_TRUE(script.MatchesUrl(GURL("bar"))); -} - -TEST(GreasemonkeySlaveTest, Parse3) { - const std::string text( - "// ==UserScript==\n" - "// @include *foo*\n" - "// ==/UserScript=="); // no trailing newline - - GreasemonkeyScript script("foo"); - script.Parse(text); - EXPECT_EQ(1U, script.include_patterns_.size()); - EXPECT_EQ(text, script.GetBody()); - EXPECT_TRUE(script.MatchesUrl(GURL("http://foo.com/bar"))); - EXPECT_FALSE(script.MatchesUrl(GURL("http://baz.org"))); -} diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 5aa1dc6..311e865 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -13,9 +13,9 @@ #include "chrome/common/notification_service.h" #include "chrome/plugin/plugin_channel.h" #include "chrome/renderer/net/render_dns_master.h" -#include "chrome/renderer/greasemonkey_slave.h" #include "chrome/renderer/render_process.h" #include "chrome/renderer/render_view.h" +#include "chrome/renderer/user_script_slave.h" #include "chrome/renderer/visitedlink_slave.h" #include "webkit/glue/cache_manager.h" @@ -34,7 +34,7 @@ RenderThread::RenderThread(const std::wstring& channel_name) channel_name_(channel_name), owner_loop_(MessageLoop::current()), visited_link_slave_(NULL), - greasemonkey_slave_(NULL), + user_script_slave_(NULL), render_dns_master_(NULL), in_send_(0) { DCHECK(owner_loop_); @@ -105,7 +105,7 @@ void RenderThread::Init() { CoInitialize(0); visited_link_slave_ = new VisitedLinkSlave(); - greasemonkey_slave_ = new GreasemonkeySlave(); + user_script_slave_ = new UserScriptSlave(); render_dns_master_.reset(new RenderDnsMaster()); @@ -134,8 +134,8 @@ void RenderThread::CleanUp() { delete visited_link_slave_; visited_link_slave_ = NULL; - delete greasemonkey_slave_; - greasemonkey_slave_ = NULL; + delete user_script_slave_; + user_script_slave_ = NULL; CoUninitialize(); } @@ -145,10 +145,10 @@ void RenderThread::OnUpdateVisitedLinks(base::SharedMemoryHandle table) { visited_link_slave_->Init(table); } -void RenderThread::OnUpdateGreasemonkeyScripts( +void RenderThread::OnUpdateUserScripts( base::SharedMemoryHandle scripts) { DCHECK(scripts) << "Bad scripts handle"; - greasemonkey_slave_->UpdateScripts(scripts); + user_script_slave_->UpdateScripts(scripts); } void RenderThread::OnMessageReceived(const IPC::Message& msg) { @@ -165,8 +165,8 @@ void RenderThread::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(ViewMsg_GetCacheResourceStats, OnGetCacheResourceStats) IPC_MESSAGE_HANDLER(ViewMsg_PluginMessage, OnPluginMessage) - IPC_MESSAGE_HANDLER(ViewMsg_Greasemonkey_NewScripts, - OnUpdateGreasemonkeyScripts) + IPC_MESSAGE_HANDLER(ViewMsg_UserScripts_NewScripts, + OnUpdateUserScripts) // send the rest to the router IPC_MESSAGE_UNHANDLED(router_.OnMessageReceived(msg)) IPC_END_MESSAGE_MAP() diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index c9dbea4..7884e9f 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -19,7 +19,7 @@ class VisitedLinkSlave; struct WebPreferences; class RenderDnsMaster; class NotificationService; -class GreasemonkeySlave; +class UserScriptSlave; // The RenderThreadBase is the minimal interface that a RenderView/Widget // expects from a render thread. The interface basically abstracts a way to send @@ -70,8 +70,8 @@ class RenderThread : public IPC::Channel::Listener, // Gets the VisitedLinkSlave instance for this thread VisitedLinkSlave* visited_link_slave() const { return visited_link_slave_; } - // Gets the GreasemonkeySlave instance for this thread - GreasemonkeySlave* greasemonkey_slave() const { return greasemonkey_slave_; } + // Gets the UserScriptSlave instance for this thread + UserScriptSlave* user_script_slave() const { return user_script_slave_; } // Do DNS prefetch resolution of a hostname. void Resolve(const char* name, size_t length); @@ -96,7 +96,7 @@ class RenderThread : public IPC::Channel::Listener, private: void OnUpdateVisitedLinks(base::SharedMemoryHandle table); - void OnUpdateGreasemonkeyScripts(base::SharedMemoryHandle table); + void OnUpdateUserScripts(base::SharedMemoryHandle table); void OnPluginMessage(const FilePath& plugin_path, const std::vector& data); @@ -128,7 +128,7 @@ class RenderThread : public IPC::Channel::Listener, // These objects live solely on the render thread. VisitedLinkSlave* visited_link_slave_; - GreasemonkeySlave* greasemonkey_slave_; + UserScriptSlave* user_script_slave_; scoped_ptr render_dns_master_; diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 295e73a..5b328c3 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -28,9 +28,9 @@ #include "chrome/renderer/about_handler.h" #include "chrome/renderer/chrome_plugin_host.h" #include "chrome/renderer/debug_message_handler.h" -#include "chrome/renderer/greasemonkey_slave.h" #include "chrome/renderer/localized_error.h" #include "chrome/renderer/renderer_resources.h" +#include "chrome/renderer/user_script_slave.h" #include "chrome/renderer/visitedlink_slave.h" #include "chrome/renderer/webmediaplayer_delegate_impl.h" #include "chrome/renderer/webplugin_delegate_proxy.h" @@ -162,7 +162,7 @@ RenderView::RenderView(RenderThreadBase* render_thread) disable_popup_blocking_(false), has_unload_listener_(false), decrement_shared_popup_at_destruction_(false), - greasemonkey_enabled_(false), + user_scripts_enabled_(false), waiting_for_create_window_ack_(false), form_field_autofill_request_id_(0), popup_notification_visible_(false), @@ -294,8 +294,8 @@ void RenderView::Init(HWND parent_hwnd, command_line.HasSwitch(switches::kDomAutomationController); disable_popup_blocking_ = command_line.HasSwitch(switches::kDisablePopupBlocking); - greasemonkey_enabled_ = - command_line.HasSwitch(switches::kEnableGreasemonkey); + user_scripts_enabled_ = + command_line.HasSwitch(switches::kEnableUserScripts); debug_message_handler_ = new DebugMessageHandler(this); render_thread_->AddFilter(debug_message_handler_); @@ -1457,15 +1457,15 @@ void RenderView::DidFinishDocumentLoadForFrame(WebView* webview, // Check whether we have new encoding name. UpdateEncoding(frame, webview->GetMainFrameEncodingName()); - // Inject any Greasemonkey scripts. Do not inject into chrome UI pages, but - // do inject into any other document. - if (greasemonkey_enabled_) { + // Inject any user scripts. Do not inject into chrome UI pages, but do inject + // into any other document. + if (user_scripts_enabled_) { const GURL &gurl = frame->GetURL(); if (g_render_thread && // Will be NULL when testing. (gurl.SchemeIs("file") || gurl.SchemeIs("http") || gurl.SchemeIs("https"))) { - g_render_thread->greasemonkey_slave()->InjectScripts(frame); + g_render_thread->user_script_slave()->InjectScripts(frame); } } } diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index d60cd2c..76e8e0e 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -692,8 +692,8 @@ class RenderView : public RenderWidget, // maintains the cache and other features of the accessibility tree. scoped_ptr glue_accessibility_; - // True if Greasemonkey is enabled in this process. - bool greasemonkey_enabled_; + // True if user scripts are enabled in this process. + bool user_scripts_enabled_; // Resource message queue. Used to queue up resource IPCs if we need // to wait for an ACK from the browser before proceeding. diff --git a/chrome/renderer/renderer.scons b/chrome/renderer/renderer.scons index c203bae..3f4da49 100644 --- a/chrome/renderer/renderer.scons +++ b/chrome/renderer/renderer.scons @@ -34,7 +34,6 @@ input_files = [ 'dom_ui_bindings.cc', 'external_host_bindings.cc', 'external_js_object.cc', - 'greasemonkey_slave.cc', 'localized_error.cc', 'plugin_channel_host.cc', 'render_process.cc', @@ -43,6 +42,7 @@ input_files = [ 'render_widget.cc', 'renderer_glue.cc', 'renderer_main.cc', + 'user_script_slave.cc', 'visitedlink_slave.cc', 'webmediaplayer_delegate_impl.cc', 'webplugin_delegate_proxy.cc', diff --git a/chrome/renderer/renderer.vcproj b/chrome/renderer/renderer.vcproj index 395809c..449c1da 100644 --- a/chrome/renderer/renderer.vcproj +++ b/chrome/renderer/renderer.vcproj @@ -202,14 +202,6 @@ > - - - - @@ -286,6 +278,14 @@ > + + + + diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc new file mode 100644 index 0000000..80cc329 --- /dev/null +++ b/chrome/renderer/user_script_slave.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/user_script_slave.h" + +#include "base/logging.h" +#include "base/pickle.h" +#include "base/shared_memory.h" +#include "googleurl/src/gurl.h" + + +// UserScript + +void UserScript::Parse(const StringPiece& script_text) { + ParseMetadata(script_text); + + // TODO(aa): Set body to just the part after the metadata block? This would + // significantly cut down on the size of the injected script in some cases. + // Would require remembering the line number the body begins at, for correct + // error line number reporting. + body_ = script_text; +} + +bool UserScript::MatchesUrl(const GURL& url) { + for (std::vector::iterator pattern = include_patterns_.begin(); + pattern != include_patterns_.end(); ++pattern) { + if (MatchPattern(url.spec(), *pattern)) { + return true; + } + } + + return false; +} + +void UserScript::ParseMetadata(const StringPiece& script_text) { + // http://wiki.greasespot.net/Metadata_block + StringPiece line; + size_t line_start = 0; + size_t line_end = 0; + bool in_metadata = false; + + static const StringPiece kUserScriptBegin("// ==UserScript=="); + static const StringPiece kUserScriptEng("// ==/UserScript=="); + static const StringPiece kIncludeDeclaration("// @include "); + + while (line_start < script_text.length()) { + line_end = script_text.find('\n', line_start); + + // Handle the case where there is no trailing newline in the file. + if (line_end == std::string::npos) { + line_end = script_text.length() - 1; + } + + line.set(script_text.data() + line_start, line_end - line_start); + + if (!in_metadata) { + if (line.starts_with(kUserScriptBegin)) { + in_metadata = true; + } + } else { + if (line.starts_with(kUserScriptEng)) { + break; + } + + if (line.starts_with(kIncludeDeclaration)) { + std::string pattern(line.data() + kIncludeDeclaration.length(), + line.length() - kIncludeDeclaration.length()); + std::string pattern_trimmed; + TrimWhitespace(pattern, TRIM_ALL, &pattern_trimmed); + AddInclude(pattern_trimmed); + } + + // TODO(aa): Handle more types of metadata. + } + + line_start = line_end + 1; + } + + // If no @include patterns were specified, default to @include *. + // This is what Greasemonkey does. + if (include_patterns_.size() == 0) { + AddInclude("*"); + } +} + +void UserScript::AddInclude(const std::string &glob_pattern) { + include_patterns_.push_back(EscapeGlob(glob_pattern)); +} + +std::string UserScript::EscapeGlob(const std::string& input_pattern) { + std::string output_pattern; + + for (size_t i = 0; i < input_pattern.length(); ++i) { + switch (input_pattern[i]) { + // These characters have special meaning to the MatchPattern() function, + // so we escape them. + case '\\': + case '?': + output_pattern += '\\'; + // fall through + + default: + output_pattern += input_pattern[i]; + } + } + + return output_pattern; +} + + +// UserScriptSlave +UserScriptSlave::UserScriptSlave() : shared_memory_(NULL) { +} + +bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { + scripts_.clear(); + + // Create the shared memory object (read only). + shared_memory_.reset(new base::SharedMemory(shared_memory, true)); + if (!shared_memory_.get()) + return false; + + // First get the size of the memory block. + if (!shared_memory_->Map(sizeof(Pickle::Header))) + return false; + Pickle::Header* pickle_header = + reinterpret_cast(shared_memory_->memory()); + + // Now map in the rest of the block. + int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size; + shared_memory_->Unmap(); + if (!shared_memory_->Map(pickle_size)) + return false; + + // Unpickle scripts. + void* iter = NULL; + int num_scripts = 0; + Pickle pickle(reinterpret_cast(shared_memory_->memory()), + pickle_size); + pickle.ReadInt(&iter, &num_scripts); + + for (int i = 0; i < num_scripts; ++i) { + const char* url = NULL; + int url_length = 0; + const char* body = NULL; + int body_length = 0; + + pickle.ReadData(&iter, &url, &url_length); + pickle.ReadData(&iter, &body, &body_length); + + scripts_.push_back(UserScript(StringPiece(url, url_length))); + UserScript& script = scripts_.back(); + script.Parse(StringPiece(body, body_length)); + } + + return true; +} + +bool UserScriptSlave::InjectScripts(WebFrame* frame) { + for (std::vector::iterator script = scripts_.begin(); + script != scripts_.end(); ++script) { + if (script->MatchesUrl(frame->GetURL())) { + frame->ExecuteJavaScript(script->GetBody().as_string(), + GURL(script->GetURL().as_string())); + } + } + + return true; +} diff --git a/chrome/renderer/user_script_slave.h b/chrome/renderer/user_script_slave.h new file mode 100644 index 0000000..dd40960 --- /dev/null +++ b/chrome/renderer/user_script_slave.h @@ -0,0 +1,95 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_SLAVE_H_ +#define CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_SLAVE_H_ + +#include "base/scoped_ptr.h" +#include "base/shared_memory.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest_prod.h" +#include "webkit/glue/webframe.h" + + +// Parsed representation of a user script. +class UserScript { + public: + UserScript(const StringPiece& script_url) + : url_(script_url) {} + + // Gets the script body that should be injected into matching content. + const StringPiece& GetBody() const { + return body_; + } + + // Gets a URL where this script can be found. + const StringPiece& GetURL() const { + return url_; + } + + // Parses the text content of a user script file. + void Parse(const StringPiece& script_text); + + // Returns true if the script should be applied to the specified URL, false + // otherwise. + bool MatchesUrl(const GURL& url); + + private: + FRIEND_TEST(UserScriptSlaveTest, EscapeGlob); + FRIEND_TEST(UserScriptSlaveTest, Parse1); + FRIEND_TEST(UserScriptSlaveTest, Parse2); + FRIEND_TEST(UserScriptSlaveTest, Parse3); + + // Helper function to convert the user script glob format to the patterns + // used internally to test URLs. + static std::string EscapeGlob(const std::string& glob); + + // Parses the metadata block from the script. + void ParseMetadata(const StringPiece& script_text); + + // Adds an include pattern that will be checked to determine whether to + // include a script on a given page. + void AddInclude(const std::string &glob_pattern); + + // The body of the script, which will be injected into content pages. This + // references shared_memory_, and is valid until that memory is either + // deleted or Unmap()'d. + StringPiece body_; + + // The url of the file the script came from. This references shared_memory_, + // and is valid until that memory is either deleted or Unmap()'d. + StringPiece url_; + + // List of patterns to test URLs against for this script. These patterns have + // been escaped for use with MatchPattern() in string_utils ('?' and '\' are + // escaped). + std::vector include_patterns_; +}; + + +// Manages installed UserScripts for a render process. +class UserScriptSlave { + public: + UserScriptSlave(); + + // Update the parsed scripts from shared memory. + bool UpdateScripts(base::SharedMemoryHandle shared_memory); + + // Inject the appropriate scripts into a frame based on its URL. + // TODO(aa): Extract a UserScriptFrame interface out of this to improve + // testability. + bool InjectScripts(WebFrame* frame); + + private: + // Shared memory containing raw script data. + scoped_ptr shared_memory_; + + // Parsed script data. + std::vector scripts_; + + DISALLOW_COPY_AND_ASSIGN(UserScriptSlave); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_SLAVE_H_ diff --git a/chrome/renderer/user_script_slave_unittest.cc b/chrome/renderer/user_script_slave_unittest.cc new file mode 100644 index 0000000..57d34a9 --- /dev/null +++ b/chrome/renderer/user_script_slave_unittest.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "chrome/renderer/user_script_slave.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(UserScriptSlaveTest, EscapeGlob) { + EXPECT_EQ("", UserScript::EscapeGlob("")); + EXPECT_EQ("*", UserScript::EscapeGlob("*")); + EXPECT_EQ("www.google.com", UserScript::EscapeGlob("www.google.com")); + EXPECT_EQ("*google.com*", UserScript::EscapeGlob("*google.com*")); + EXPECT_EQ("foo\\\\bar\\?hot=dog", + UserScript::EscapeGlob("foo\\bar?hot=dog")); +} + +TEST(UserScriptSlaveTest, Parse1) { + const std::string text( + "// This is my awesome script\n" + "// It does stuff.\n" + "// ==UserScript== trailing garbage\n" + "// @name foobar script\n" + "// @namespace http://www.google.com/\n" + "// @include *mail.google.com*\n" + "// \n" + "// @othergarbage\n" + "// @include *mail.yahoo.com*\r\n" + "// @include \t *mail.msn.com*\n" // extra spaces after "@include" OK + "//@include not-recognized\n" // must have one space after "//" + "// ==/UserScript== trailing garbage\n" + "\n" + "\n" + "alert('hoo!');\n"); + + UserScript script("foo"); + script.Parse(text); + EXPECT_EQ(3U, script.include_patterns_.size()); + EXPECT_EQ(text, script.GetBody()); + EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.google.com"))); + EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.google.com/foo"))); + EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.yahoo.com/bar"))); + EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.msn.com/baz"))); + EXPECT_FALSE(script.MatchesUrl(GURL("http://www.hotmail.com"))); +} + +TEST(UserScriptSlaveTest, Parse2) { + const std::string text("default to @include *"); + + UserScript script("foo"); + script.Parse(text); + EXPECT_EQ(1U, script.include_patterns_.size()); + EXPECT_EQ(text, script.GetBody()); + EXPECT_TRUE(script.MatchesUrl(GURL("foo"))); + EXPECT_TRUE(script.MatchesUrl(GURL("bar"))); +} + +TEST(UserScriptSlaveTest, Parse3) { + const std::string text( + "// ==UserScript==\n" + "// @include *foo*\n" + "// ==/UserScript=="); // no trailing newline + + UserScript script("foo"); + script.Parse(text); + EXPECT_EQ(1U, script.include_patterns_.size()); + EXPECT_EQ(text, script.GetBody()); + EXPECT_TRUE(script.MatchesUrl(GURL("http://foo.com/bar"))); + EXPECT_FALSE(script.MatchesUrl(GURL("http://baz.org"))); +} diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h index c99b51c..37e6c17 100644 --- a/chrome/test/testing_profile.h +++ b/chrome/test/testing_profile.h @@ -70,7 +70,7 @@ class TestingProfile : public Profile { virtual ExtensionsService* GetExtensionsService() { return NULL; } - virtual GreasemonkeyMaster* GetGreasemonkeyMaster() { + virtual UserScriptMaster* GetUserScriptMaster() { return NULL; } virtual HistoryService* GetHistoryService(ServiceAccessType access) { diff --git a/chrome/test/unit/unit_tests.scons b/chrome/test/unit/unit_tests.scons index 4fe3332..a192cab 100644 --- a/chrome/test/unit/unit_tests.scons +++ b/chrome/test/unit/unit_tests.scons @@ -114,7 +114,7 @@ unit_test_files.extend([ '$CHROME_DIR/common/l10n_util_unittest.cc', '$CHROME_DIR/common/property_bag_unittest.cc', '$CHROME_DIR/common/unzip_unittest.cc', - '$CHROME_DIR/renderer/greasemonkey_slave_unittest.cc', + '$CHROME_DIR/renderer/user_script_slave_unittest.cc', '$CHROME_DIR/browser/history/visit_database_unittest.cc', ]) @@ -170,9 +170,9 @@ if env.Bit('windows'): '$CHROME_DIR/browser/download/download_manager_unittest.cc', '$CHROME_DIR/browser/download/download_request_manager_unittest.cc', '$CHROME_DIR/browser/download/save_package_unittest.cc', + '$CHROME_DIR/browser/extensions/user_script_master_unittest.cc', '$CHROME_DIR/browser/encryptor_unittest.cc', '$CHROME_DIR/browser/google_url_tracker_unittest.cc', - '$CHROME_DIR/browser/greasemonkey_master_unittest.cc', '$CHROME_DIR/browser/history/expire_history_backend_unittest.cc', '$CHROME_DIR/browser/history/history_backend_unittest.cc', '$CHROME_DIR/browser/history/history_querying_unittest.cc', diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj index d8baadc..448a848 100644 --- a/chrome/test/unit/unittests.vcproj +++ b/chrome/test/unit/unittests.vcproj @@ -1055,30 +1055,26 @@ - - -- cgit v1.1