diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-25 05:08:54 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-25 05:08:54 +0000 |
commit | bdbc87ca87fda52c3262240705d974030e9ed4b4 (patch) | |
tree | 6ca34f4c2ba9fe4b340075206846c115a5400d27 /chrome/browser/extensions | |
parent | 1692ea885338dd799ae9f47151d2a7fd4d1b10c4 (diff) | |
download | chromium_src-bdbc87ca87fda52c3262240705d974030e9ed4b4.zip chromium_src-bdbc87ca87fda52c3262240705d974030e9ed4b4.tar.gz chromium_src-bdbc87ca87fda52c3262240705d974030e9ed4b4.tar.bz2 |
Add user script support to extensions.
This is implemented mostly by relying on the existing
user script code. But since extension user scripts are
declared, not discovered in a directory, I had to add
support for adding 'lone' user scripts to
UserScriptMaster. This led to a bit of refactoring.
Note that this CL relies on:
http://codereview.chromium.org/18352
Review URL: http://codereview.chromium.org/18198
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8614 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 25 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.h | 7 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master.cc | 120 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master.h | 52 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master_unittest.cc | 2 |
5 files changed, 140 insertions, 66 deletions
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 85d4fe8..e103821 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -9,6 +9,7 @@ #include "base/string_util.h" #include "base/thread.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/user_script_master.h" #include "chrome/common/json_value_serializer.h" #include "chrome/common/notification_service.h" @@ -17,10 +18,12 @@ const FilePath::CharType* ExtensionsService::kInstallDirectoryName = FILE_PATH_LITERAL("Extensions"); -ExtensionsService::ExtensionsService(const FilePath& profile_directory) +ExtensionsService::ExtensionsService(const FilePath& profile_directory, + UserScriptMaster* user_script_master) : message_loop_(MessageLoop::current()), backend_(new ExtensionsServiceBackend), - install_directory_(profile_directory.Append(kInstallDirectoryName)) { + install_directory_(profile_directory.Append(kInstallDirectoryName)), + user_script_master_(user_script_master) { } ExtensionsService::~ExtensionsService() { @@ -53,6 +56,24 @@ void ExtensionsService::OnExtensionsLoadedFromDirectory( extensions_.insert(extensions_.end(), new_extensions->begin(), new_extensions->end()); + // Tell UserScriptMaster about any scripts in the loaded extensions. + for (ExtensionList::iterator extension = extensions_.begin(); + extension != extensions_.end(); ++extension) { + const UserScriptList& scripts = (*extension)->user_scripts(); + for (UserScriptList::const_iterator script = scripts.begin(); + script != scripts.end(); ++script) { + user_script_master_->AddLoneScript(*script); + } + } + + // Tell UserScriptMaster to also watch the extensions directory for changes + // and then kick off the first scan. + // TODO(aa): This should go away when we implement the --extension flag, since + // developing scripts in the Extensions directory will no longer be a common + // use-case. + user_script_master_->AddWatchedPath(install_directory_); + user_script_master_->StartScan(); + NotificationService::current()->Notify(NOTIFY_EXTENSIONS_LOADED, NotificationService::AllSources(), Details<ExtensionList>(new_extensions)); diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index ed5a81c..1a78d0a 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -15,6 +15,7 @@ typedef std::vector<Extension*> ExtensionList; class ExtensionsServiceBackend; +class UserScriptMaster; // Interface for the frontend to implement. Typically, this will be // ExtensionsService, but it can also be a test harness. @@ -38,7 +39,8 @@ class ExtensionsServiceFrontendInterface // Manages installed and running Chromium extensions. class ExtensionsService : public ExtensionsServiceFrontendInterface { public: - ExtensionsService(const FilePath& profile_directory); + ExtensionsService(const FilePath& profile_directory, + UserScriptMaster* user_script_master); ~ExtensionsService(); // Gets the list of currently installed extensions. @@ -71,6 +73,9 @@ class ExtensionsService : public ExtensionsServiceFrontendInterface { // The full path to the directory where extensions are installed. FilePath install_directory_; + // The user script master for this profile. + scoped_refptr<UserScriptMaster> user_script_master_; + DISALLOW_COPY_AND_ASSIGN(ExtensionsService); }; diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index 6efb577..66a8d70 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -13,7 +13,7 @@ #include "base/pickle.h" #include "base/string_util.h" #include "chrome/common/notification_service.h" -#include "googleurl/src/gurl.h" +#include "chrome/common/stl_util-inl.h" #include "net/base/net_util.h" // Defined in extension.h. @@ -74,15 +74,15 @@ void UserScriptMaster::ScriptReloader::ParseMetadataHeader( } void UserScriptMaster::ScriptReloader::StartScan( - MessageLoop* work_loop, - const FilePath& script_dir) { + MessageLoop* work_loop, const FilePath& script_dir, + const UserScriptList& lone_scripts) { // 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)); + script_dir, lone_scripts)); } void UserScriptMaster::ScriptReloader::NotifyMaster( @@ -99,8 +99,9 @@ void UserScriptMaster::ScriptReloader::NotifyMaster( Release(); } -void UserScriptMaster::ScriptReloader::RunScan(const FilePath script_dir) { - base::SharedMemory* shared_memory = GetNewScripts(script_dir); +void UserScriptMaster::ScriptReloader::RunScan( + const FilePath script_dir, const UserScriptList lone_scripts) { + base::SharedMemory* shared_memory = GetNewScripts(script_dir, lone_scripts); // Post the new scripts back to the master's message loop. master_message_loop_->PostTask(FROM_HERE, @@ -110,45 +111,47 @@ void UserScriptMaster::ScriptReloader::RunScan(const FilePath script_dir) { } base::SharedMemory* UserScriptMaster::ScriptReloader::GetNewScripts( - const FilePath& script_dir) { - std::vector<std::wstring> 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()); + const FilePath& script_dir, const UserScriptList& lone_scripts) { + UserScriptList all_scripts; + + // Find all the scripts in |script_dir|. + if (!script_dir.value().empty()) { + 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()) { + all_scripts.push_back(UserScriptInfo()); + UserScriptInfo& info = all_scripts.back(); + info.url = GURL(std::string(kUserScriptURLScheme) + ":/" + + net::FilePathToFileURL(file.ToWStringHack()).ExtractFileName()); + info.path = file; + } } - if (scripts.empty()) + if (all_scripts.empty() && lone_scripts.empty()) return NULL; - // Pickle scripts data. - Pickle pickle; - pickle.WriteSize(scripts.size()); - for (std::vector<std::wstring>::iterator path = scripts.begin(); - path != scripts.end(); ++path) { - std::string url(kUserScriptURLScheme); - url += ":/"; - url += net::FilePathToFileURL(*path).ExtractFileName(); + // Add all the lone scripts. + all_scripts.insert(all_scripts.end(), lone_scripts.begin(), + lone_scripts.end()); - std::string contents; + // Load and pickle each script. Look for a metadata header if there are no + // matches specified already. + Pickle pickle; + pickle.WriteSize(all_scripts.size()); + for (UserScriptList::iterator iter = all_scripts.begin(); + iter != all_scripts.end(); ++iter) { // TODO(aa): Support unicode script files. - file_util::ReadFileToString(*path, &contents); - - std::vector<std::string> includes; - ParseMetadataHeader(contents, &includes); - - // Write scripts as 'data' so that we can read it out in the slave without - // allocating a new string. - pickle.WriteData(url.c_str(), url.length()); - pickle.WriteData(contents.c_str(), contents.length()); - pickle.WriteSize(includes.size()); - for (std::vector<std::string>::iterator iter = includes.begin(); - iter != includes.end(); ++iter) { - pickle.WriteString(*iter); + std::string contents; + file_util::ReadFileToString(iter->path.ToWStringHack(), &contents); + + if (iter->matches.empty()) { + // TODO(aa): Handle errors parsing header. + ParseMetadataHeader(contents, &iter->matches); } + + PickleScriptData(*iter, contents, &pickle); } // Create the shared memory object. @@ -171,22 +174,45 @@ base::SharedMemory* UserScriptMaster::ScriptReloader::GetNewScripts( return shared_memory.release(); } +void UserScriptMaster::ScriptReloader::PickleScriptData( + const UserScriptInfo& script, const std::string& contents, Pickle* pickle) { + // Write scripts as 'data' so that we can read it out in the slave without + // allocating a new string. + pickle->WriteData(script.url.spec().c_str(), script.url.spec().length()); + pickle->WriteData(contents.c_str(), contents.length()); + pickle->WriteSize(script.matches.size()); + for (std::vector<std::string>::const_iterator iter = script.matches.begin(); + iter != script.matches.end(); ++iter) { + pickle->WriteString(*iter); + } +} + UserScriptMaster::UserScriptMaster(MessageLoop* worker_loop, - const FilePath& script_dir) - : user_script_dir_(new FilePath(script_dir)), - dir_watcher_(new DirectoryWatcher), + const FilePath& script_dir) + : user_script_dir_(script_dir), 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(); - } + if (!user_script_dir_.value().empty()) + AddWatchedPath(script_dir); } UserScriptMaster::~UserScriptMaster() { if (script_reloader_) script_reloader_->DisownMaster(); + +// TODO(aa): Enable this when DirectoryWatcher is implemented for linux and mac. +#if defined(OS_WIN) + STLDeleteElements(&dir_watchers_); +#endif +} + +void UserScriptMaster::AddWatchedPath(const FilePath& path) { +// TODO(aa): Enable this when DirectoryWatcher is implemented for linux and mac. +#if defined(OS_WIN) + DirectoryWatcher* watcher = new DirectoryWatcher(); + watcher->Watch(path, this); + dir_watchers_.push_back(watcher); +#endif } void UserScriptMaster::NewScriptsAvailable(base::SharedMemory* handle) { @@ -225,5 +251,5 @@ void UserScriptMaster::StartScan() { if (!script_reloader_) script_reloader_ = new ScriptReloader(this); - script_reloader_->StartScan(worker_loop_, *user_script_dir_); + script_reloader_->StartScan(worker_loop_, user_script_dir_, lone_scripts_); } diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h index 5167a65..1c1b095 100644 --- a/chrome/browser/extensions/user_script_master.h +++ b/chrome/browser/extensions/user_script_master.h @@ -14,6 +14,8 @@ #include "base/string_piece.h" #include "googleurl/src/gurl.h" +class Pickle; + struct UserScriptInfo { GURL url; FilePath path; @@ -32,6 +34,19 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, UserScriptMaster(MessageLoop* worker, const FilePath& script_dir); ~UserScriptMaster(); + // Add a single user script that exists outside the script directory. + void AddLoneScript(const UserScriptInfo& script) { + lone_scripts_.push_back(script); + } + + // Add a watched directory. All scripts will be reloaded when any file in + // this directory changes. + void AddWatchedPath(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(); + // Gets the segment of shared memory for the scripts. base::SharedMemory* GetSharedMemory() const { return shared_memory_.get(); @@ -44,7 +59,7 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, 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_; } + FilePath user_script_dir() const { return user_script_dir_; } private: FRIEND_TEST(UserScriptMasterTest, Parse1); @@ -69,7 +84,8 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, // Start a scan for scripts. // Will always send a message to the master upon completion. - void StartScan(MessageLoop* work_loop, const FilePath& script_dir); + void StartScan(MessageLoop* work_loop, const FilePath& script_dir, + const UserScriptList &external_scripts); // The master is going away; don't call it back. void DisownMaster() { @@ -89,15 +105,20 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, 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); + // Scan the specified directory and lone scripts, calling NotifyMaster when + // done. The parameters are intentionally passed by value so their lifetimes + // aren't tied to the caller. + void RunScan(const FilePath script_dir, const UserScriptList lone_scripts); // 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); + // Scan the script directory and lone scripts, returning either a new + // SharedMemory or NULL on error. + base::SharedMemory* GetNewScripts(const FilePath& script_dir, + const UserScriptList& lone_scripts); + + // Serialize script metadata and contents into the specified pickle. + void PickleScriptData(const UserScriptInfo& script, + const std::string& contents, Pickle* pickle); // A pointer back to our master. // May be NULL if DisownMaster() is called. @@ -113,15 +134,11 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, // 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<FilePath> user_script_dir_; + // The directories containing user scripts. + FilePath user_script_dir_; // The watcher watches the profile's user scripts directory for new scripts. - scoped_ptr<DirectoryWatcher> dir_watcher_; + std::vector<DirectoryWatcher*> dir_watchers_; // The MessageLoop that the scanner worker runs on. // Typically the file thread; configurable for testing. @@ -134,6 +151,9 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, // Contains the scripts that were found the last time scripts were updated. scoped_ptr<base::SharedMemory> shared_memory_; + // List of scripts outside of script directories we should also load. + UserScriptList lone_scripts_; + // 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. diff --git a/chrome/browser/extensions/user_script_master_unittest.cc b/chrome/browser/extensions/user_script_master_unittest.cc index 0fa1932..f0f2dc2 100644 --- a/chrome/browser/extensions/user_script_master_unittest.cc +++ b/chrome/browser/extensions/user_script_master_unittest.cc @@ -74,6 +74,7 @@ TEST_F(UserScriptMasterTest, NoScripts) { scoped_refptr<UserScriptMaster> master( new UserScriptMaster(MessageLoop::current(), script_dir_)); + master->StartScan(); message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); message_loop_.Run(); @@ -110,6 +111,7 @@ TEST_F(UserScriptMasterTest, ExistingScripts) { scoped_refptr<UserScriptMaster> master( new UserScriptMaster(MessageLoop::current(), script_dir_)); + master->StartScan(); message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); message_loop_.Run(); |