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 |