| OLD | NEW |
| 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/user_script_master.h" | 5 #include "chrome/browser/extensions/user_script_master.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
| 10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/path_service.h" | 12 #include "base/path_service.h" |
| 13 #include "base/pickle.h" | 13 #include "base/pickle.h" |
| 14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
| 15 #include "chrome/common/notification_service.h" | 15 #include "chrome/common/notification_service.h" |
| 16 #include "googleurl/src/gurl.h" | 16 #include "chrome/common/stl_util-inl.h" |
| 17 #include "net/base/net_util.h" | 17 #include "net/base/net_util.h" |
| 18 | 18 |
| 19 // Defined in extension.h. | 19 // Defined in extension.h. |
| 20 extern const char kExtensionURLScheme[]; | 20 extern const char kExtensionURLScheme[]; |
| 21 extern const char kUserScriptURLScheme[]; | 21 extern const char kUserScriptURLScheme[]; |
| 22 | 22 |
| 23 // static | 23 // static |
| 24 void UserScriptMaster::ScriptReloader::ParseMetadataHeader( | 24 void UserScriptMaster::ScriptReloader::ParseMetadataHeader( |
| 25 const StringPiece& script_text, std::vector<std::string> *includes) { | 25 const StringPiece& script_text, std::vector<std::string> *includes) { |
| 26 // http://wiki.greasespot.net/Metadata_block | 26 // http://wiki.greasespot.net/Metadata_block |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 } | 67 } |
| 68 | 68 |
| 69 // If no @include patterns were specified, default to @include *. | 69 // If no @include patterns were specified, default to @include *. |
| 70 // This is what Greasemonkey does. | 70 // This is what Greasemonkey does. |
| 71 if (includes->size() == 0) { | 71 if (includes->size() == 0) { |
| 72 includes->push_back("*"); | 72 includes->push_back("*"); |
| 73 } | 73 } |
| 74 } | 74 } |
| 75 | 75 |
| 76 void UserScriptMaster::ScriptReloader::StartScan( | 76 void UserScriptMaster::ScriptReloader::StartScan( |
| 77 MessageLoop* work_loop, | 77 MessageLoop* work_loop, const FilePath& script_dir, |
| 78 const FilePath& script_dir) { | 78 const UserScriptList& lone_scripts) { |
| 79 // Add a reference to ourselves to keep ourselves alive while we're running. | 79 // Add a reference to ourselves to keep ourselves alive while we're running. |
| 80 // Balanced by NotifyMaster(). | 80 // Balanced by NotifyMaster(). |
| 81 AddRef(); | 81 AddRef(); |
| 82 work_loop->PostTask(FROM_HERE, | 82 work_loop->PostTask(FROM_HERE, |
| 83 NewRunnableMethod(this, | 83 NewRunnableMethod(this, |
| 84 &UserScriptMaster::ScriptReloader::RunScan, | 84 &UserScriptMaster::ScriptReloader::RunScan, |
| 85 script_dir)); | 85 script_dir, lone_scripts)); |
| 86 } | 86 } |
| 87 | 87 |
| 88 void UserScriptMaster::ScriptReloader::NotifyMaster( | 88 void UserScriptMaster::ScriptReloader::NotifyMaster( |
| 89 base::SharedMemory* memory) { | 89 base::SharedMemory* memory) { |
| 90 if (!master_) { | 90 if (!master_) { |
| 91 // The master went away, so these new scripts aren't useful anymore. | 91 // The master went away, so these new scripts aren't useful anymore. |
| 92 delete memory; | 92 delete memory; |
| 93 } else { | 93 } else { |
| 94 master_->NewScriptsAvailable(memory); | 94 master_->NewScriptsAvailable(memory); |
| 95 } | 95 } |
| 96 | 96 |
| 97 // Drop our self-reference. | 97 // Drop our self-reference. |
| 98 // Balances StartScan(). | 98 // Balances StartScan(). |
| 99 Release(); | 99 Release(); |
| 100 } | 100 } |
| 101 | 101 |
| 102 void UserScriptMaster::ScriptReloader::RunScan(const FilePath script_dir) { | 102 void UserScriptMaster::ScriptReloader::RunScan( |
| 103 base::SharedMemory* shared_memory = GetNewScripts(script_dir); | 103 const FilePath script_dir, const UserScriptList lone_scripts) { |
| 104 base::SharedMemory* shared_memory = GetNewScripts(script_dir, lone_scripts); |
| 104 | 105 |
| 105 // Post the new scripts back to the master's message loop. | 106 // Post the new scripts back to the master's message loop. |
| 106 master_message_loop_->PostTask(FROM_HERE, | 107 master_message_loop_->PostTask(FROM_HERE, |
| 107 NewRunnableMethod(this, | 108 NewRunnableMethod(this, |
| 108 &UserScriptMaster::ScriptReloader::NotifyMaster, | 109 &UserScriptMaster::ScriptReloader::NotifyMaster, |
| 109 shared_memory)); | 110 shared_memory)); |
| 110 } | 111 } |
| 111 | 112 |
| 112 base::SharedMemory* UserScriptMaster::ScriptReloader::GetNewScripts( | 113 base::SharedMemory* UserScriptMaster::ScriptReloader::GetNewScripts( |
| 113 const FilePath& script_dir) { | 114 const FilePath& script_dir, const UserScriptList& lone_scripts) { |
| 114 std::vector<std::wstring> scripts; | 115 UserScriptList all_scripts; |
| 115 | 116 |
| 116 file_util::FileEnumerator enumerator(script_dir, false, | 117 // Find all the scripts in |script_dir|. |
| 117 file_util::FileEnumerator::FILES, | 118 if (!script_dir.value().empty()) { |
| 118 FILE_PATH_LITERAL("*.user.js")); | 119 file_util::FileEnumerator enumerator(script_dir, false, |
| 119 for (FilePath file = enumerator.Next(); !file.value().empty(); | 120 file_util::FileEnumerator::FILES, |
| 120 file = enumerator.Next()) { | 121 FILE_PATH_LITERAL("*.user.js")); |
| 121 scripts.push_back(file.ToWStringHack()); | 122 for (FilePath file = enumerator.Next(); !file.value().empty(); |
| 122 } | 123 file = enumerator.Next()) { |
| 123 | 124 all_scripts.push_back(UserScriptInfo()); |
| 124 if (scripts.empty()) | 125 UserScriptInfo& info = all_scripts.back(); |
| 125 return NULL; | 126 info.url = GURL(std::string(kUserScriptURLScheme) + ":/" + |
| 126 | 127 net::FilePathToFileURL(file.ToWStringHack()).ExtractFileName()); |
| 127 // Pickle scripts data. | 128 info.path = file; |
| 128 Pickle pickle; | |
| 129 pickle.WriteSize(scripts.size()); | |
| 130 for (std::vector<std::wstring>::iterator path = scripts.begin(); | |
| 131 path != scripts.end(); ++path) { | |
| 132 std::string url(kUserScriptURLScheme); | |
| 133 url += ":/"; | |
| 134 url += net::FilePathToFileURL(*path).ExtractFileName(); | |
| 135 | |
| 136 std::string contents; | |
| 137 // TODO(aa): Support unicode script files. | |
| 138 file_util::ReadFileToString(*path, &contents); | |
| 139 | |
| 140 std::vector<std::string> includes; | |
| 141 ParseMetadataHeader(contents, &includes); | |
| 142 | |
| 143 // Write scripts as 'data' so that we can read it out in the slave without | |
| 144 // allocating a new string. | |
| 145 pickle.WriteData(url.c_str(), url.length()); | |
| 146 pickle.WriteData(contents.c_str(), contents.length()); | |
| 147 pickle.WriteSize(includes.size()); | |
| 148 for (std::vector<std::string>::iterator iter = includes.begin(); | |
| 149 iter != includes.end(); ++iter) { | |
| 150 pickle.WriteString(*iter); | |
| 151 } | 129 } |
| 152 } | 130 } |
| 153 | 131 |
| 132 if (all_scripts.empty() && lone_scripts.empty()) |
| 133 return NULL; |
| 134 |
| 135 // Add all the lone scripts. |
| 136 all_scripts.insert(all_scripts.end(), lone_scripts.begin(), |
| 137 lone_scripts.end()); |
| 138 |
| 139 // Load and pickle each script. Look for a metadata header if there are no |
| 140 // matches specified already. |
| 141 Pickle pickle; |
| 142 pickle.WriteSize(all_scripts.size()); |
| 143 for (UserScriptList::iterator iter = all_scripts.begin(); |
| 144 iter != all_scripts.end(); ++iter) { |
| 145 // TODO(aa): Support unicode script files. |
| 146 std::string contents; |
| 147 file_util::ReadFileToString(iter->path.ToWStringHack(), &contents); |
| 148 |
| 149 if (iter->matches.empty()) { |
| 150 // TODO(aa): Handle errors parsing header. |
| 151 ParseMetadataHeader(contents, &iter->matches); |
| 152 } |
| 153 |
| 154 PickleScriptData(*iter, contents, &pickle); |
| 155 } |
| 156 |
| 154 // Create the shared memory object. | 157 // Create the shared memory object. |
| 155 scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); | 158 scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); |
| 156 | 159 |
| 157 if (!shared_memory->Create(std::wstring(), // anonymous | 160 if (!shared_memory->Create(std::wstring(), // anonymous |
| 158 false, // read-only | 161 false, // read-only |
| 159 false, // open existing | 162 false, // open existing |
| 160 pickle.size())) { | 163 pickle.size())) { |
| 161 return NULL; | 164 return NULL; |
| 162 } | 165 } |
| 163 | 166 |
| 164 // Map into our process. | 167 // Map into our process. |
| 165 if (!shared_memory->Map(pickle.size())) | 168 if (!shared_memory->Map(pickle.size())) |
| 166 return NULL; | 169 return NULL; |
| 167 | 170 |
| 168 // Copy the pickle to shared memory. | 171 // Copy the pickle to shared memory. |
| 169 memcpy(shared_memory->memory(), pickle.data(), pickle.size()); | 172 memcpy(shared_memory->memory(), pickle.data(), pickle.size()); |
| 170 | 173 |
| 171 return shared_memory.release(); | 174 return shared_memory.release(); |
| 172 } | 175 } |
| 173 | 176 |
| 177 void UserScriptMaster::ScriptReloader::PickleScriptData( |
| 178 const UserScriptInfo& script, const std::string& contents, Pickle* pickle) { |
| 179 // Write scripts as 'data' so that we can read it out in the slave without |
| 180 // allocating a new string. |
| 181 pickle->WriteData(script.url.spec().c_str(), script.url.spec().length()); |
| 182 pickle->WriteData(contents.c_str(), contents.length()); |
| 183 pickle->WriteSize(script.matches.size()); |
| 184 for (std::vector<std::string>::const_iterator iter = script.matches.begin(); |
| 185 iter != script.matches.end(); ++iter) { |
| 186 pickle->WriteString(*iter); |
| 187 } |
| 188 } |
| 189 |
| 174 UserScriptMaster::UserScriptMaster(MessageLoop* worker_loop, | 190 UserScriptMaster::UserScriptMaster(MessageLoop* worker_loop, |
| 175 const FilePath& script_dir) | 191 const FilePath& script_dir) |
| 176 : user_script_dir_(new FilePath(script_dir)), | 192 : user_script_dir_(script_dir), |
| 177 dir_watcher_(new DirectoryWatcher), | |
| 178 worker_loop_(worker_loop), | 193 worker_loop_(worker_loop), |
| 179 pending_scan_(false) { | 194 pending_scan_(false) { |
| 180 // Watch our scripts directory for modifications. | 195 if (!user_script_dir_.value().empty()) |
| 181 if (dir_watcher_->Watch(script_dir, this)) { | 196 AddWatchedPath(script_dir); |
| 182 // (Asynchronously) scan for our initial set of scripts. | |
| 183 StartScan(); | |
| 184 } | |
| 185 } | 197 } |
| 186 | 198 |
| 187 UserScriptMaster::~UserScriptMaster() { | 199 UserScriptMaster::~UserScriptMaster() { |
| 188 if (script_reloader_) | 200 if (script_reloader_) |
| 189 script_reloader_->DisownMaster(); | 201 script_reloader_->DisownMaster(); |
| 202 |
| 203 // TODO(aa): Enable this when DirectoryWatcher is implemented for linux and mac. |
| 204 #if defined(OS_WIN) |
| 205 STLDeleteElements(&dir_watchers_); |
| 206 #endif |
| 207 } |
| 208 |
| 209 void UserScriptMaster::AddWatchedPath(const FilePath& path) { |
| 210 // TODO(aa): Enable this when DirectoryWatcher is implemented for linux and mac. |
| 211 #if defined(OS_WIN) |
| 212 DirectoryWatcher* watcher = new DirectoryWatcher(); |
| 213 watcher->Watch(path, this); |
| 214 dir_watchers_.push_back(watcher); |
| 215 #endif |
| 190 } | 216 } |
| 191 | 217 |
| 192 void UserScriptMaster::NewScriptsAvailable(base::SharedMemory* handle) { | 218 void UserScriptMaster::NewScriptsAvailable(base::SharedMemory* handle) { |
| 193 // Ensure handle is deleted or released. | 219 // Ensure handle is deleted or released. |
| 194 scoped_ptr<base::SharedMemory> handle_deleter(handle); | 220 scoped_ptr<base::SharedMemory> handle_deleter(handle); |
| 195 | 221 |
| 196 if (pending_scan_) { | 222 if (pending_scan_) { |
| 197 // While we were scanning, there were further changes. Don't bother | 223 // While we were scanning, there were further changes. Don't bother |
| 198 // notifying about these scripts and instead just immediately rescan. | 224 // notifying about these scripts and instead just immediately rescan. |
| 199 pending_scan_ = false; | 225 pending_scan_ = false; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 218 return; | 244 return; |
| 219 } | 245 } |
| 220 | 246 |
| 221 StartScan(); | 247 StartScan(); |
| 222 } | 248 } |
| 223 | 249 |
| 224 void UserScriptMaster::StartScan() { | 250 void UserScriptMaster::StartScan() { |
| 225 if (!script_reloader_) | 251 if (!script_reloader_) |
| 226 script_reloader_ = new ScriptReloader(this); | 252 script_reloader_ = new ScriptReloader(this); |
| 227 | 253 |
| 228 script_reloader_->StartScan(worker_loop_, *user_script_dir_); | 254 script_reloader_->StartScan(worker_loop_, user_script_dir_, lone_scripts_); |
| 229 } | 255 } |
| OLD | NEW |