OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 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 <map> |
7 #include <string> | 8 #include <string> |
8 #include <vector> | 9 #include <vector> |
9 | 10 |
10 #include "base/file_path.h" | 11 #include "base/file_path.h" |
11 #include "base/file_util.h" | 12 #include "base/file_util.h" |
12 #include "base/pickle.h" | 13 #include "base/pickle.h" |
13 #include "base/stl_util.h" | 14 #include "base/stl_util.h" |
14 #include "base/string_util.h" | 15 #include "base/string_util.h" |
15 #include "base/threading/thread.h" | 16 #include "base/threading/thread.h" |
16 #include "base/version.h" | 17 #include "base/version.h" |
17 #include "chrome/browser/extensions/extension_service.h" | 18 #include "chrome/browser/extensions/extension_service.h" |
18 #include "chrome/browser/profiles/profile.h" | 19 #include "chrome/browser/profiles/profile.h" |
19 #include "chrome/common/chrome_notification_types.h" | 20 #include "chrome/common/chrome_notification_types.h" |
20 #include "chrome/common/extensions/extension.h" | 21 #include "chrome/common/extensions/extension.h" |
21 #include "chrome/common/extensions/extension_messages.h" | 22 #include "chrome/common/extensions/extension_file_util.h" |
| 23 #include "chrome/common/extensions/extension_message_bundle.h" |
22 #include "chrome/common/extensions/extension_resource.h" | 24 #include "chrome/common/extensions/extension_resource.h" |
| 25 #include "chrome/common/extensions/extension_set.h" |
23 #include "content/browser/renderer_host/render_process_host.h" | 26 #include "content/browser/renderer_host/render_process_host.h" |
24 #include "content/common/notification_service.h" | 27 #include "content/common/notification_service.h" |
25 | 28 |
26 // Helper function to parse greasesmonkey headers | 29 // Helper function to parse greasesmonkey headers |
27 static bool GetDeclarationValue(const base::StringPiece& line, | 30 static bool GetDeclarationValue(const base::StringPiece& line, |
28 const base::StringPiece& prefix, | 31 const base::StringPiece& prefix, |
29 std::string* value) { | 32 std::string* value) { |
30 base::StringPiece::size_type index = line.find(prefix); | 33 base::StringPiece::size_type index = line.find(prefix); |
31 if (index == base::StringPiece::npos) | 34 if (index == base::StringPiece::npos) |
32 return false; | 35 return false; |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 | 133 |
131 // If no patterns were specified, default to @include *. This is what | 134 // If no patterns were specified, default to @include *. This is what |
132 // Greasemonkey does. | 135 // Greasemonkey does. |
133 if (script->globs().empty() && script->url_patterns().is_empty()) | 136 if (script->globs().empty() && script->url_patterns().is_empty()) |
134 script->add_glob("*"); | 137 script->add_glob("*"); |
135 | 138 |
136 return true; | 139 return true; |
137 } | 140 } |
138 | 141 |
139 void UserScriptMaster::ScriptReloader::StartLoad( | 142 void UserScriptMaster::ScriptReloader::StartLoad( |
140 const UserScriptList& user_scripts) { | 143 const UserScriptList& user_scripts, |
| 144 const ExtensionsInfo& extensions_info_) { |
141 // Add a reference to ourselves to keep ourselves alive while we're running. | 145 // Add a reference to ourselves to keep ourselves alive while we're running. |
142 // Balanced by NotifyMaster(). | 146 // Balanced by NotifyMaster(). |
143 AddRef(); | 147 AddRef(); |
| 148 |
| 149 this->extensions_info_ = extensions_info_; |
144 BrowserThread::PostTask( | 150 BrowserThread::PostTask( |
145 BrowserThread::FILE, FROM_HERE, | 151 BrowserThread::FILE, FROM_HERE, |
146 NewRunnableMethod( | 152 NewRunnableMethod( |
147 this, &UserScriptMaster::ScriptReloader::RunLoad, user_scripts)); | 153 this, &UserScriptMaster::ScriptReloader::RunLoad, user_scripts)); |
148 } | 154 } |
149 | 155 |
150 void UserScriptMaster::ScriptReloader::NotifyMaster( | 156 void UserScriptMaster::ScriptReloader::NotifyMaster( |
151 base::SharedMemory* memory) { | 157 base::SharedMemory* memory) { |
152 // The master went away, so these new scripts aren't useful anymore. | 158 // The master went away, so these new scripts aren't useful anymore. |
153 if (!master_) | 159 if (!master_) |
154 delete memory; | 160 delete memory; |
155 else | 161 else |
156 master_->NewScriptsAvailable(memory); | 162 master_->NewScriptsAvailable(memory); |
157 | 163 |
158 // Drop our self-reference. | 164 // Drop our self-reference. |
159 // Balances StartLoad(). | 165 // Balances StartLoad(). |
160 Release(); | 166 Release(); |
161 } | 167 } |
162 | 168 |
163 static bool LoadScriptContent(UserScript::File* script_file) { | 169 static bool LoadScriptContent(UserScript::File* script_file, |
| 170 const SubstitutionMap* localization_messages) { |
164 std::string content; | 171 std::string content; |
165 const FilePath& path = ExtensionResource::GetFilePath( | 172 const FilePath& path = ExtensionResource::GetFilePath( |
166 script_file->extension_root(), script_file->relative_path()); | 173 script_file->extension_root(), script_file->relative_path()); |
167 if (path.empty()) { | 174 if (path.empty()) { |
168 LOG(WARNING) << "Failed to get file path to " | 175 LOG(WARNING) << "Failed to get file path to " |
169 << script_file->relative_path().value() << " from " | 176 << script_file->relative_path().value() << " from " |
170 << script_file->extension_root().value(); | 177 << script_file->extension_root().value(); |
171 return false; | 178 return false; |
172 } | 179 } |
173 if (!file_util::ReadFileToString(path, &content)) { | 180 if (!file_util::ReadFileToString(path, &content)) { |
174 LOG(WARNING) << "Failed to load user script file: " << path.value(); | 181 LOG(WARNING) << "Failed to load user script file: " << path.value(); |
175 return false; | 182 return false; |
176 } | 183 } |
177 | 184 |
| 185 // Localize the content. |
| 186 if (localization_messages) { |
| 187 std::string error; |
| 188 ExtensionMessageBundle::ReplaceMessagesWithExternalDictionary( |
| 189 *localization_messages, &content, &error); |
| 190 if (!error.empty()) { |
| 191 LOG(WARNING) << "Failed to replace messages in script: " << error; |
| 192 } |
| 193 } |
| 194 |
178 // Remove BOM from the content. | 195 // Remove BOM from the content. |
179 std::string::size_type index = content.find(kUtf8ByteOrderMark); | 196 std::string::size_type index = content.find(kUtf8ByteOrderMark); |
180 if (index == 0) { | 197 if (index == 0) { |
181 script_file->set_content(content.substr(strlen(kUtf8ByteOrderMark))); | 198 script_file->set_content(content.substr(strlen(kUtf8ByteOrderMark))); |
182 } else { | 199 } else { |
183 script_file->set_content(content); | 200 script_file->set_content(content); |
184 } | 201 } |
185 | 202 |
186 return true; | 203 return true; |
187 } | 204 } |
188 | 205 |
189 // static | |
190 void UserScriptMaster::ScriptReloader::LoadUserScripts( | 206 void UserScriptMaster::ScriptReloader::LoadUserScripts( |
191 UserScriptList* user_scripts) { | 207 UserScriptList* user_scripts) { |
192 for (size_t i = 0; i < user_scripts->size(); ++i) { | 208 for (size_t i = 0; i < user_scripts->size(); ++i) { |
193 UserScript& script = user_scripts->at(i); | 209 UserScript& script = user_scripts->at(i); |
| 210 scoped_ptr<SubstitutionMap> localization_messages( |
| 211 GetLocalizationMessages(script.extension_id())); |
194 for (size_t k = 0; k < script.js_scripts().size(); ++k) { | 212 for (size_t k = 0; k < script.js_scripts().size(); ++k) { |
195 UserScript::File& script_file = script.js_scripts()[k]; | 213 UserScript::File& script_file = script.js_scripts()[k]; |
196 if (script_file.GetContent().empty()) | 214 if (script_file.GetContent().empty()) |
197 LoadScriptContent(&script_file); | 215 LoadScriptContent(&script_file, NULL); |
198 } | 216 } |
199 for (size_t k = 0; k < script.css_scripts().size(); ++k) { | 217 for (size_t k = 0; k < script.css_scripts().size(); ++k) { |
200 UserScript::File& script_file = script.css_scripts()[k]; | 218 UserScript::File& script_file = script.css_scripts()[k]; |
201 if (script_file.GetContent().empty()) | 219 if (script_file.GetContent().empty()) |
202 LoadScriptContent(&script_file); | 220 LoadScriptContent(&script_file, localization_messages.get()); |
203 } | 221 } |
204 } | 222 } |
205 } | 223 } |
206 | 224 |
| 225 SubstitutionMap* UserScriptMaster::ScriptReloader::GetLocalizationMessages( |
| 226 std::string extension_id) { |
| 227 if (extensions_info_.find(extension_id) == extensions_info_.end()) { |
| 228 return NULL; |
| 229 } |
| 230 |
| 231 return extension_file_util::LoadExtensionMessageBundleSubstitutionMap( |
| 232 extensions_info_[extension_id].first, |
| 233 extension_id, |
| 234 extensions_info_[extension_id].second); |
| 235 } |
| 236 |
207 // Pickle user scripts and return pointer to the shared memory. | 237 // Pickle user scripts and return pointer to the shared memory. |
208 static base::SharedMemory* Serialize(const UserScriptList& scripts) { | 238 static base::SharedMemory* Serialize(const UserScriptList& scripts) { |
209 Pickle pickle; | 239 Pickle pickle; |
210 pickle.WriteSize(scripts.size()); | 240 pickle.WriteSize(scripts.size()); |
211 for (size_t i = 0; i < scripts.size(); i++) { | 241 for (size_t i = 0; i < scripts.size(); i++) { |
212 const UserScript& script = scripts[i]; | 242 const UserScript& script = scripts[i]; |
213 // TODO(aa): This can be replaced by sending content script metadata to | 243 // TODO(aa): This can be replaced by sending content script metadata to |
214 // renderers along with other extension data in ExtensionMsg_Loaded. | 244 // renderers along with other extension data in ExtensionMsg_Loaded. |
215 // See crbug.com/70516. | 245 // See crbug.com/70516. |
216 script.Pickle(&pickle); | 246 script.Pickle(&pickle); |
(...skipping 14 matching lines...) Expand all Loading... |
231 | 261 |
232 if (!shared_memory->CreateAndMapAnonymous(pickle.size())) | 262 if (!shared_memory->CreateAndMapAnonymous(pickle.size())) |
233 return NULL; | 263 return NULL; |
234 | 264 |
235 // Copy the pickle to shared memory. | 265 // Copy the pickle to shared memory. |
236 memcpy(shared_memory->memory(), pickle.data(), pickle.size()); | 266 memcpy(shared_memory->memory(), pickle.data(), pickle.size()); |
237 | 267 |
238 return shared_memory.release(); | 268 return shared_memory.release(); |
239 } | 269 } |
240 | 270 |
241 // This method will be called from the file thread | 271 // This method will be called on the file thread. |
242 void UserScriptMaster::ScriptReloader::RunLoad( | 272 void UserScriptMaster::ScriptReloader::RunLoad( |
243 const UserScriptList& user_scripts) { | 273 const UserScriptList& user_scripts) { |
244 LoadUserScripts(const_cast<UserScriptList*>(&user_scripts)); | 274 LoadUserScripts(const_cast<UserScriptList*>(&user_scripts)); |
245 | 275 |
246 // Scripts now contains list of up-to-date scripts. Load the content in the | 276 // Scripts now contains list of up-to-date scripts. Load the content in the |
247 // shared memory and let the master know it's ready. We need to post the task | 277 // shared memory and let the master know it's ready. We need to post the task |
248 // back even if no scripts ware found to balance the AddRef/Release calls | 278 // back even if no scripts ware found to balance the AddRef/Release calls. |
249 BrowserThread::PostTask( | 279 BrowserThread::PostTask( |
250 master_thread_id_, FROM_HERE, | 280 master_thread_id_, FROM_HERE, |
251 NewRunnableMethod( | 281 NewRunnableMethod( |
252 this, &ScriptReloader::NotifyMaster, Serialize(user_scripts))); | 282 this, &ScriptReloader::NotifyMaster, Serialize(user_scripts))); |
253 } | 283 } |
254 | 284 |
255 | 285 |
256 UserScriptMaster::UserScriptMaster(Profile* profile) | 286 UserScriptMaster::UserScriptMaster(Profile* profile) |
257 : extensions_service_ready_(false), | 287 : extensions_service_ready_(false), |
258 pending_load_(false), | 288 pending_load_(false), |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 const NotificationDetails& details) { | 334 const NotificationDetails& details) { |
305 bool should_start_load = false; | 335 bool should_start_load = false; |
306 switch (type) { | 336 switch (type) { |
307 case chrome::NOTIFICATION_EXTENSIONS_READY: | 337 case chrome::NOTIFICATION_EXTENSIONS_READY: |
308 extensions_service_ready_ = true; | 338 extensions_service_ready_ = true; |
309 should_start_load = true; | 339 should_start_load = true; |
310 break; | 340 break; |
311 case chrome::NOTIFICATION_EXTENSION_LOADED: { | 341 case chrome::NOTIFICATION_EXTENSION_LOADED: { |
312 // Add any content scripts inside the extension. | 342 // Add any content scripts inside the extension. |
313 const Extension* extension = Details<const Extension>(details).ptr(); | 343 const Extension* extension = Details<const Extension>(details).ptr(); |
| 344 extensions_info_[extension->id()] = |
| 345 ExtensionSet::ExtensionPathAndDefaultLocale( |
| 346 extension->path(), extension->default_locale()); |
314 bool incognito_enabled = profile_->GetExtensionService()-> | 347 bool incognito_enabled = profile_->GetExtensionService()-> |
315 IsIncognitoEnabled(extension->id()); | 348 IsIncognitoEnabled(extension->id()); |
316 const UserScriptList& scripts = extension->content_scripts(); | 349 const UserScriptList& scripts = extension->content_scripts(); |
317 for (UserScriptList::const_iterator iter = scripts.begin(); | 350 for (UserScriptList::const_iterator iter = scripts.begin(); |
318 iter != scripts.end(); ++iter) { | 351 iter != scripts.end(); ++iter) { |
319 user_scripts_.push_back(*iter); | 352 user_scripts_.push_back(*iter); |
320 user_scripts_.back().set_incognito_enabled(incognito_enabled); | 353 user_scripts_.back().set_incognito_enabled(incognito_enabled); |
321 } | 354 } |
322 if (extensions_service_ready_) | 355 if (extensions_service_ready_) |
323 should_start_load = true; | 356 should_start_load = true; |
324 break; | 357 break; |
325 } | 358 } |
326 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | 359 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { |
327 // Remove any content scripts. | 360 // Remove any content scripts. |
328 const Extension* extension = | 361 const Extension* extension = |
329 Details<UnloadedExtensionInfo>(details)->extension; | 362 Details<UnloadedExtensionInfo>(details)->extension; |
| 363 extensions_info_.erase(extension->id()); |
330 UserScriptList new_user_scripts; | 364 UserScriptList new_user_scripts; |
331 for (UserScriptList::iterator iter = user_scripts_.begin(); | 365 for (UserScriptList::iterator iter = user_scripts_.begin(); |
332 iter != user_scripts_.end(); ++iter) { | 366 iter != user_scripts_.end(); ++iter) { |
333 if (iter->extension_id() != extension->id()) | 367 if (iter->extension_id() != extension->id()) |
334 new_user_scripts.push_back(*iter); | 368 new_user_scripts.push_back(*iter); |
335 } | 369 } |
336 user_scripts_ = new_user_scripts; | 370 user_scripts_ = new_user_scripts; |
337 should_start_load = true; | 371 should_start_load = true; |
338 | 372 |
339 // TODO(aa): Do we want to do something smarter for the scripts that have | 373 // TODO(aa): Do we want to do something smarter for the scripts that have |
(...skipping 17 matching lines...) Expand all Loading... |
357 } else { | 391 } else { |
358 StartLoad(); | 392 StartLoad(); |
359 } | 393 } |
360 } | 394 } |
361 } | 395 } |
362 | 396 |
363 void UserScriptMaster::StartLoad() { | 397 void UserScriptMaster::StartLoad() { |
364 if (!script_reloader_) | 398 if (!script_reloader_) |
365 script_reloader_ = new ScriptReloader(this); | 399 script_reloader_ = new ScriptReloader(this); |
366 | 400 |
367 script_reloader_->StartLoad(user_scripts_); | 401 script_reloader_->StartLoad(user_scripts_, extensions_info_); |
368 } | 402 } |
369 | 403 |
370 void UserScriptMaster::SendUpdate(RenderProcessHost* process, | 404 void UserScriptMaster::SendUpdate(RenderProcessHost* process, |
371 base::SharedMemory* shared_memory) { | 405 base::SharedMemory* shared_memory) { |
372 Profile* profile = Profile::FromBrowserContext(process->browser_context()); | 406 Profile* profile = Profile::FromBrowserContext(process->browser_context()); |
373 // Make sure we only send user scripts to processes in our profile. | 407 // Make sure we only send user scripts to processes in our profile. |
374 if (!profile_->IsSameProfile(profile)) | 408 if (!profile_->IsSameProfile(profile)) |
375 return; | 409 return; |
376 | 410 |
377 // If the process is being started asynchronously, early return. We'll end up | 411 // If the process is being started asynchronously, early return. We'll end up |
378 // calling InitUserScripts when it's created which will call this again. | 412 // calling InitUserScripts when it's created which will call this again. |
379 base::ProcessHandle handle = process->GetHandle(); | 413 base::ProcessHandle handle = process->GetHandle(); |
380 if (!handle) | 414 if (!handle) |
381 return; | 415 return; |
382 | 416 |
383 base::SharedMemoryHandle handle_for_process; | 417 base::SharedMemoryHandle handle_for_process; |
384 if (!shared_memory->ShareToProcess(handle, &handle_for_process)) | 418 if (!shared_memory->ShareToProcess(handle, &handle_for_process)) |
385 return; // This can legitimately fail if the renderer asserts at startup. | 419 return; // This can legitimately fail if the renderer asserts at startup. |
386 | 420 |
387 if (base::SharedMemory::IsHandleValid(handle_for_process)) | 421 if (base::SharedMemory::IsHandleValid(handle_for_process)) |
388 process->Send(new ExtensionMsg_UpdateUserScripts(handle_for_process)); | 422 process->Send(new ExtensionMsg_UpdateUserScripts(handle_for_process)); |
389 } | 423 } |
OLD | NEW |