Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "extensions/browser/user_script_loader.h" | 5 #include "extensions/browser/user_script_loader.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/bind.h" | |
| 11 #include "base/bind_helpers.h" | |
| 12 #include "base/files/file_path.h" | |
| 13 #include "base/files/file_util.h" | |
| 14 #include "base/version.h" | 10 #include "base/version.h" |
| 15 #include "content/public/browser/browser_context.h" | 11 #include "content/public/browser/browser_context.h" |
| 16 #include "content/public/browser/browser_thread.h" | 12 #include "content/public/browser/browser_thread.h" |
| 17 #include "content/public/browser/notification_service.h" | 13 #include "content/public/browser/notification_service.h" |
| 18 #include "content/public/browser/notification_types.h" | 14 #include "content/public/browser/notification_types.h" |
| 19 #include "content/public/browser/render_process_host.h" | 15 #include "content/public/browser/render_process_host.h" |
| 20 #include "extensions/browser/content_verifier.h" | |
| 21 #include "extensions/browser/extensions_browser_client.h" | 16 #include "extensions/browser/extensions_browser_client.h" |
| 22 #include "extensions/browser/notification_types.h" | 17 #include "extensions/browser/notification_types.h" |
| 23 #include "extensions/common/extension_messages.h" | 18 #include "extensions/common/extension_messages.h" |
| 24 #include "extensions/common/file_util.h" | |
| 25 | 19 |
| 26 using content::BrowserThread; | 20 using content::BrowserThread; |
| 27 using content::BrowserContext; | 21 using content::BrowserContext; |
| 28 | 22 |
| 29 namespace extensions { | 23 namespace extensions { |
| 30 | 24 |
| 31 namespace { | 25 namespace { |
| 32 | 26 |
| 33 using LoadScriptsCallback = | |
| 34 base::Callback<void(scoped_ptr<UserScriptList>, | |
| 35 scoped_ptr<base::SharedMemory>)>; | |
| 36 | |
| 37 UserScriptLoader::SubstitutionMap* GetLocalizationMessages( | |
| 38 const UserScriptLoader::HostsInfo& hosts_info, | |
| 39 const HostID& host_id) { | |
| 40 UserScriptLoader::HostsInfo::const_iterator iter = hosts_info.find(host_id); | |
| 41 if (iter == hosts_info.end()) | |
| 42 return nullptr; | |
| 43 return file_util::LoadMessageBundleSubstitutionMap( | |
| 44 iter->second.first, host_id.id(), iter->second.second); | |
| 45 } | |
| 46 | |
| 47 void LoadUserScripts( | |
| 48 UserScriptList* user_scripts, | |
| 49 const UserScriptLoader::HostsInfo& hosts_info, | |
| 50 const std::set<int>& added_script_ids, | |
| 51 const scoped_refptr<ContentVerifier>& verifier, | |
| 52 UserScriptLoader::LoadUserScriptsContentFunction callback) { | |
| 53 for (UserScriptList::iterator script = user_scripts->begin(); | |
| 54 script != user_scripts->end(); | |
| 55 ++script) { | |
| 56 if (added_script_ids.count(script->id()) == 0) | |
| 57 continue; | |
| 58 scoped_ptr<UserScriptLoader::SubstitutionMap> localization_messages( | |
| 59 GetLocalizationMessages(hosts_info, script->host_id())); | |
| 60 for (size_t k = 0; k < script->js_scripts().size(); ++k) { | |
| 61 UserScript::File& script_file = script->js_scripts()[k]; | |
| 62 if (script_file.GetContent().empty()) | |
| 63 callback.Run(script->host_id(), &script_file, NULL, verifier); | |
| 64 } | |
| 65 for (size_t k = 0; k < script->css_scripts().size(); ++k) { | |
| 66 UserScript::File& script_file = script->css_scripts()[k]; | |
| 67 if (script_file.GetContent().empty()) | |
| 68 callback.Run(script->host_id(), &script_file, | |
| 69 localization_messages.get(), verifier); | |
| 70 } | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 // Pickle user scripts and return pointer to the shared memory. | |
| 75 scoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) { | |
| 76 Pickle pickle; | |
| 77 pickle.WriteSizeT(scripts.size()); | |
| 78 for (UserScriptList::const_iterator script = scripts.begin(); | |
| 79 script != scripts.end(); | |
| 80 ++script) { | |
| 81 // TODO(aa): This can be replaced by sending content script metadata to | |
| 82 // renderers along with other extension data in ExtensionMsg_Loaded. | |
| 83 // See crbug.com/70516. | |
| 84 script->Pickle(&pickle); | |
| 85 // Write scripts as 'data' so that we can read it out in the slave without | |
| 86 // allocating a new string. | |
| 87 for (size_t j = 0; j < script->js_scripts().size(); j++) { | |
| 88 base::StringPiece contents = script->js_scripts()[j].GetContent(); | |
| 89 pickle.WriteData(contents.data(), contents.length()); | |
| 90 } | |
| 91 for (size_t j = 0; j < script->css_scripts().size(); j++) { | |
| 92 base::StringPiece contents = script->css_scripts()[j].GetContent(); | |
| 93 pickle.WriteData(contents.data(), contents.length()); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 // Create the shared memory object. | |
| 98 base::SharedMemory shared_memory; | |
| 99 | |
| 100 base::SharedMemoryCreateOptions options; | |
| 101 options.size = pickle.size(); | |
| 102 options.share_read_only = true; | |
| 103 if (!shared_memory.Create(options)) | |
| 104 return scoped_ptr<base::SharedMemory>(); | |
| 105 | |
| 106 if (!shared_memory.Map(pickle.size())) | |
| 107 return scoped_ptr<base::SharedMemory>(); | |
| 108 | |
| 109 // Copy the pickle to shared memory. | |
| 110 memcpy(shared_memory.memory(), pickle.data(), pickle.size()); | |
| 111 | |
| 112 base::SharedMemoryHandle readonly_handle; | |
| 113 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(), | |
| 114 &readonly_handle)) | |
| 115 return scoped_ptr<base::SharedMemory>(); | |
| 116 | |
| 117 return make_scoped_ptr(new base::SharedMemory(readonly_handle, | |
| 118 /*read_only=*/true)); | |
| 119 } | |
| 120 | |
| 121 void LoadScriptsOnFileThread( | |
| 122 scoped_ptr<UserScriptList> user_scripts, | |
| 123 const UserScriptLoader::HostsInfo& hosts_info, | |
| 124 const std::set<int>& added_script_ids, | |
| 125 const scoped_refptr<ContentVerifier>& verifier, | |
| 126 UserScriptLoader::LoadUserScriptsContentFunction function, | |
| 127 LoadScriptsCallback callback) { | |
| 128 DCHECK(user_scripts.get()); | |
| 129 LoadUserScripts(user_scripts.get(), hosts_info, added_script_ids, | |
| 130 verifier, function); | |
| 131 scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts); | |
| 132 BrowserThread::PostTask( | |
| 133 BrowserThread::UI, | |
| 134 FROM_HERE, | |
| 135 base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory))); | |
| 136 } | |
| 137 | |
| 138 // Helper function to parse greasesmonkey headers | 27 // Helper function to parse greasesmonkey headers |
| 139 bool GetDeclarationValue(const base::StringPiece& line, | 28 bool GetDeclarationValue(const base::StringPiece& line, |
| 140 const base::StringPiece& prefix, | 29 const base::StringPiece& prefix, |
| 141 std::string* value) { | 30 std::string* value) { |
| 142 base::StringPiece::size_type index = line.find(prefix); | 31 base::StringPiece::size_type index = line.find(prefix); |
| 143 if (index == base::StringPiece::npos) | 32 if (index == base::StringPiece::npos) |
| 144 return false; | 33 return false; |
| 145 | 34 |
| 146 std::string temp(line.data() + index + prefix.length(), | 35 std::string temp(line.data() + index + prefix.length(), |
| 147 line.length() - index - prefix.length()); | 36 line.length() - index - prefix.length()); |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 } | 132 } |
| 244 | 133 |
| 245 // If no patterns were specified, default to @include *. This is what | 134 // If no patterns were specified, default to @include *. This is what |
| 246 // Greasemonkey does. | 135 // Greasemonkey does. |
| 247 if (script->globs().empty() && script->url_patterns().is_empty()) | 136 if (script->globs().empty() && script->url_patterns().is_empty()) |
| 248 script->add_glob("*"); | 137 script->add_glob("*"); |
| 249 | 138 |
| 250 return true; | 139 return true; |
| 251 } | 140 } |
| 252 | 141 |
| 253 void UserScriptLoader::LoadScriptsForTest(UserScriptList* user_scripts) { | 142 UserScriptLoader::UserScriptLoader(BrowserContext* browser_context, |
| 254 HostsInfo info; | 143 const HostID& host_id) |
| 255 std::set<int> added_script_ids; | |
| 256 for (UserScriptList::iterator it = user_scripts->begin(); | |
| 257 it != user_scripts->end(); | |
| 258 ++it) { | |
| 259 added_script_ids.insert(it->id()); | |
| 260 } | |
| 261 LoadUserScripts(user_scripts, info, added_script_ids, | |
| 262 NULL /* no verifier for testing */, | |
| 263 GetLoadUserScriptsFunction()); | |
| 264 } | |
| 265 | |
| 266 UserScriptLoader::UserScriptLoader( | |
| 267 BrowserContext* browser_context, | |
| 268 const HostID& host_id, | |
| 269 const scoped_refptr<ContentVerifier>& content_verifier) | |
| 270 : user_scripts_(new UserScriptList()), | 144 : user_scripts_(new UserScriptList()), |
| 271 clear_scripts_(false), | 145 clear_scripts_(false), |
| 272 ready_(false), | 146 ready_(false), |
| 273 pending_load_(false), | 147 pending_load_(false), |
| 274 browser_context_(browser_context), | 148 browser_context_(browser_context), |
| 275 host_id_(host_id), | 149 host_id_(host_id) { |
| 276 content_verifier_(content_verifier), | |
| 277 weak_factory_(this) { | |
| 278 registrar_.Add(this, | 150 registrar_.Add(this, |
| 279 content::NOTIFICATION_RENDERER_PROCESS_CREATED, | 151 content::NOTIFICATION_RENDERER_PROCESS_CREATED, |
| 280 content::NotificationService::AllBrowserContextsAndSources()); | 152 content::NotificationService::AllBrowserContextsAndSources()); |
| 281 } | 153 } |
| 282 | 154 |
| 283 UserScriptLoader::~UserScriptLoader() { | 155 UserScriptLoader::~UserScriptLoader() { |
| 284 } | 156 } |
| 285 | 157 |
| 286 void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) { | 158 void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) { |
| 287 for (std::set<UserScript>::const_iterator it = scripts.begin(); | 159 for (std::set<UserScript>::const_iterator it = scripts.begin(); |
| 288 it != scripts.end(); | 160 it != scripts.end(); |
| 289 ++it) { | 161 ++it) { |
| 290 removed_scripts_.erase(*it); | 162 removed_scripts_.erase(*it); |
| 291 added_scripts_.insert(*it); | 163 added_scripts_.insert(*it); |
| 292 } | 164 } |
| 293 AttemptLoad(); | 165 AttemptLoad(); |
| 294 } | 166 } |
| 295 | 167 |
| 168 void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts, | |
| 169 int render_process_id, | |
| 170 int render_view_id) { | |
| 171 AddScripts(scripts); | |
| 172 } | |
| 173 | |
| 296 void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) { | 174 void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) { |
| 297 for (std::set<UserScript>::const_iterator it = scripts.begin(); | 175 for (std::set<UserScript>::const_iterator it = scripts.begin(); |
| 298 it != scripts.end(); | 176 it != scripts.end(); |
| 299 ++it) { | 177 ++it) { |
| 300 added_scripts_.erase(*it); | 178 added_scripts_.erase(*it); |
| 301 removed_scripts_.insert(*it); | 179 removed_scripts_.insert(*it); |
| 302 } | 180 } |
| 303 AttemptLoad(); | 181 AttemptLoad(); |
| 304 } | 182 } |
| 305 | 183 |
| 306 void UserScriptLoader::ClearScripts() { | 184 void UserScriptLoader::ClearScripts(bool is_clear) { |
| 307 clear_scripts_ = true; | 185 clear_scripts_ = is_clear; |
| 308 added_scripts_.clear(); | 186 added_scripts_.clear(); |
| 309 removed_scripts_.clear(); | 187 removed_scripts_.clear(); |
| 310 AttemptLoad(); | 188 if (!clear_scripts_) |
| 189 user_scripts_.reset(nullptr); | |
| 190 else | |
| 191 AttemptLoad(); | |
| 311 } | 192 } |
| 312 | 193 |
| 313 void UserScriptLoader::Observe(int type, | 194 void UserScriptLoader::Observe(int type, |
| 314 const content::NotificationSource& source, | 195 const content::NotificationSource& source, |
| 315 const content::NotificationDetails& details) { | 196 const content::NotificationDetails& details) { |
| 316 DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED); | 197 DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED); |
| 317 content::RenderProcessHost* process = | 198 content::RenderProcessHost* process = |
| 318 content::Source<content::RenderProcessHost>(source).ptr(); | 199 content::Source<content::RenderProcessHost>(source).ptr(); |
| 319 if (!ExtensionsBrowserClient::Get()->IsSameContext( | 200 if (!ExtensionsBrowserClient::Get()->IsSameContext( |
| 320 browser_context_, process->GetBrowserContext())) | 201 browser_context_, process->GetBrowserContext())) |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 375 } | 256 } |
| 376 | 257 |
| 377 // Expand |changed_hosts_| for OnScriptsLoaded, which will use it in | 258 // Expand |changed_hosts_| for OnScriptsLoaded, which will use it in |
| 378 // its IPC message. This must be done before we clear |added_scripts_| and | 259 // its IPC message. This must be done before we clear |added_scripts_| and |
| 379 // |removed_scripts_| below. | 260 // |removed_scripts_| below. |
| 380 std::set<UserScript> changed_scripts(added_scripts_); | 261 std::set<UserScript> changed_scripts(added_scripts_); |
| 381 changed_scripts.insert(removed_scripts_.begin(), removed_scripts_.end()); | 262 changed_scripts.insert(removed_scripts_.begin(), removed_scripts_.end()); |
| 382 for (const UserScript& script : changed_scripts) | 263 for (const UserScript& script : changed_scripts) |
| 383 changed_hosts_.insert(script.host_id()); | 264 changed_hosts_.insert(script.host_id()); |
| 384 | 265 |
| 385 // |changed_hosts_| before passing it to LoadScriptsOnFileThread. | 266 LoadScripts(user_scripts_.Pass(), changed_hosts_, added_script_ids); |
| 386 UpdateHostsInfo(changed_hosts_); | |
| 387 | |
| 388 BrowserThread::PostTask( | |
| 389 BrowserThread::FILE, FROM_HERE, | |
| 390 base::Bind(&LoadScriptsOnFileThread, base::Passed(&user_scripts_), | |
| 391 hosts_info_, added_script_ids, content_verifier_, | |
| 392 GetLoadUserScriptsFunction(), | |
| 393 base::Bind(&UserScriptLoader::OnScriptsLoaded, | |
| 394 weak_factory_.GetWeakPtr()))); | |
| 395 | |
| 396 clear_scripts_ = false; | |
|
Devlin
2015/04/21 22:32:20
Why don't we leave these in here, so we can get ri
Xi Han
2015/04/22 15:32:11
Hmm, right, moved it back after LoadScripts were c
| |
| 397 added_scripts_.clear(); | |
| 398 removed_scripts_.clear(); | |
| 399 user_scripts_.reset(NULL); | |
| 400 } | 267 } |
| 401 | 268 |
| 402 void UserScriptLoader::AddHostInfo(const HostID& host_id, | 269 // static |
| 403 const PathAndDefaultLocale& location) { | 270 scoped_ptr<base::SharedMemory> UserScriptLoader::Serialize( |
| 404 if (hosts_info_.find(host_id) != hosts_info_.end()) | 271 const UserScriptList& scripts) { |
| 405 return; | 272 Pickle pickle; |
| 406 hosts_info_[host_id] = location; | 273 pickle.WriteSizeT(scripts.size()); |
| 407 } | 274 for (UserScriptList::const_iterator script = scripts.begin(); |
| 275 script != scripts.end(); ++script) { | |
| 276 // TODO(aa): This can be replaced by sending content script metadata to | |
| 277 // renderers along with other extension data in ExtensionMsg_Loaded. | |
| 278 // See crbug.com/70516. | |
| 279 script->Pickle(&pickle); | |
| 280 // Write scripts as 'data' so that we can read it out in the slave without | |
| 281 // allocating a new string. | |
| 282 for (size_t j = 0; j < script->js_scripts().size(); j++) { | |
| 283 base::StringPiece contents = script->js_scripts()[j].GetContent(); | |
| 284 pickle.WriteData(contents.data(), contents.length()); | |
| 285 } | |
| 286 for (size_t j = 0; j < script->css_scripts().size(); j++) { | |
| 287 base::StringPiece contents = script->css_scripts()[j].GetContent(); | |
| 288 pickle.WriteData(contents.data(), contents.length()); | |
| 289 } | |
| 290 } | |
| 408 | 291 |
| 409 void UserScriptLoader::RemoveHostInfo(const HostID& host_id) { | 292 // Create the shared memory object. |
| 410 hosts_info_.erase(host_id); | 293 base::SharedMemory shared_memory; |
| 294 | |
| 295 base::SharedMemoryCreateOptions options; | |
| 296 options.size = pickle.size(); | |
| 297 options.share_read_only = true; | |
| 298 if (!shared_memory.Create(options)) | |
| 299 return scoped_ptr<base::SharedMemory>(); | |
| 300 | |
| 301 if (!shared_memory.Map(pickle.size())) | |
| 302 return scoped_ptr<base::SharedMemory>(); | |
| 303 | |
| 304 // Copy the pickle to shared memory. | |
| 305 memcpy(shared_memory.memory(), pickle.data(), pickle.size()); | |
| 306 | |
| 307 base::SharedMemoryHandle readonly_handle; | |
| 308 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(), | |
| 309 &readonly_handle)) | |
| 310 return scoped_ptr<base::SharedMemory>(); | |
| 311 | |
| 312 return make_scoped_ptr(new base::SharedMemory(readonly_handle, | |
| 313 /*read_only=*/true)); | |
| 411 } | 314 } |
| 412 | 315 |
| 413 void UserScriptLoader::SetReady(bool ready) { | 316 void UserScriptLoader::SetReady(bool ready) { |
| 414 bool was_ready = ready_; | 317 bool was_ready = ready_; |
| 415 ready_ = ready; | 318 ready_ = ready; |
| 416 if (ready_ && !was_ready) | 319 if (ready_ && !was_ready) |
| 417 AttemptLoad(); | 320 AttemptLoad(); |
| 418 } | 321 } |
| 419 | 322 |
| 420 void UserScriptLoader::OnScriptsLoaded( | 323 void UserScriptLoader::OnScriptsLoaded( |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 479 if (!shared_memory->ShareToProcess(handle, &handle_for_process)) | 382 if (!shared_memory->ShareToProcess(handle, &handle_for_process)) |
| 480 return; // This can legitimately fail if the renderer asserts at startup. | 383 return; // This can legitimately fail if the renderer asserts at startup. |
| 481 | 384 |
| 482 if (base::SharedMemory::IsHandleValid(handle_for_process)) { | 385 if (base::SharedMemory::IsHandleValid(handle_for_process)) { |
| 483 process->Send(new ExtensionMsg_UpdateUserScripts(handle_for_process, | 386 process->Send(new ExtensionMsg_UpdateUserScripts(handle_for_process, |
| 484 host_id(), changed_hosts)); | 387 host_id(), changed_hosts)); |
| 485 } | 388 } |
| 486 } | 389 } |
| 487 | 390 |
| 488 } // namespace extensions | 391 } // namespace extensions |
| OLD | NEW |