Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Side by Side Diff: chrome/browser/extensions/user_script_loader.cc

Issue 420543002: Declarative content scripts: Browser-side: per-extension shared memory regions (lazily loaded) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Refactor UserScriptMaster into UserScriptLoader, managed by SharedUserScriptMaster and DeclarativeU… Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 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 "chrome/browser/extensions/user_script_master.h" 5 #include "chrome/browser/extensions/user_script_loader.h"
6 6
7 #include <set>
7 #include <string> 8 #include <string>
8 9
9 #include "base/bind.h" 10 #include "base/bind.h"
10 #include "base/bind_helpers.h" 11 #include "base/bind_helpers.h"
11 #include "base/file_util.h" 12 #include "base/file_util.h"
12 #include "base/files/file_path.h" 13 #include "base/files/file_path.h"
13 #include "base/memory/shared_memory.h" 14 #include "base/memory/shared_memory.h"
14 #include "base/version.h" 15 #include "base/version.h"
15 #include "chrome/browser/chrome_notification_types.h" 16 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/extension_util.h" 17 #include "chrome/browser/extensions/extension_util.h"
17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/extensions/api/i18n/default_locale_handler.h" 19 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
19 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" 20 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
21 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/notification_service.h" 22 #include "content/public/browser/notification_service.h"
21 #include "content/public/browser/render_process_host.h" 23 #include "content/public/browser/render_process_host.h"
22 #include "extensions/browser/component_extension_resource_manager.h" 24 #include "extensions/browser/component_extension_resource_manager.h"
23 #include "extensions/browser/content_verifier.h" 25 #include "extensions/browser/content_verifier.h"
24 #include "extensions/browser/extension_registry.h" 26 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_system.h" 27 #include "extensions/browser/extension_system.h"
26 #include "extensions/browser/extensions_browser_client.h" 28 #include "extensions/browser/extensions_browser_client.h"
29 #include "extensions/common/extension_messages.h"
27 #include "extensions/common/file_util.h" 30 #include "extensions/common/file_util.h"
28 #include "extensions/common/message_bundle.h" 31 #include "extensions/common/message_bundle.h"
32 #include "extensions/common/one_shot_event.h"
29 #include "ui/base/resource/resource_bundle.h" 33 #include "ui/base/resource/resource_bundle.h"
30 34
31 using content::BrowserThread; 35 using content::BrowserThread;
32 using extensions::ExtensionsBrowserClient; 36 using extensions::ExtensionsBrowserClient;
33 37
34 namespace extensions { 38 namespace extensions {
35 39
36 namespace { 40 namespace {
37 41
38 typedef base::Callback<void(scoped_ptr<UserScriptList>, 42 typedef base::Callback<
39 scoped_ptr<base::SharedMemory>)> 43 void(scoped_ptr<std::set<UserScript> >, scoped_ptr<base::SharedMemory>)>
40 LoadScriptsCallback; 44 LoadScriptsCallback;
41 45
42 void VerifyContent(scoped_refptr<ContentVerifier> verifier, 46 void VerifyContent(scoped_refptr<ContentVerifier> verifier,
43 const std::string& extension_id, 47 const ExtensionId& extension_id,
44 const base::FilePath& extension_root, 48 const base::FilePath& extension_root,
45 const base::FilePath& relative_path, 49 const base::FilePath& relative_path,
46 const std::string& content) { 50 const std::string& content) {
47 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 51 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
48 scoped_refptr<ContentVerifyJob> job( 52 scoped_refptr<ContentVerifyJob> job(
49 verifier->CreateJobFor(extension_id, extension_root, relative_path)); 53 verifier->CreateJobFor(extension_id, extension_root, relative_path));
50 if (job.get()) { 54 if (job.get()) {
51 job->Start(); 55 job->Start();
52 job->BytesRead(content.size(), content.data()); 56 job->BytesRead(content.size(), content.data());
53 job->DoneReading(); 57 job->DoneReading();
54 } 58 }
55 } 59 }
56 60
57 bool LoadScriptContent(const std::string& extension_id, 61 bool LoadScriptContent(const ExtensionId& extension_id,
58 UserScript::File* script_file, 62 UserScript::File* script_file,
59 const SubstitutionMap* localization_messages, 63 const SubstitutionMap* localization_messages,
60 scoped_refptr<ContentVerifier> verifier) { 64 scoped_refptr<ContentVerifier> verifier) {
61 std::string content; 65 std::string content;
62 const base::FilePath& path = ExtensionResource::GetFilePath( 66 const base::FilePath& path = ExtensionResource::GetFilePath(
63 script_file->extension_root(), script_file->relative_path(), 67 script_file->extension_root(),
68 script_file->relative_path(),
64 ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT); 69 ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT);
65 if (path.empty()) { 70 if (path.empty()) {
66 int resource_id; 71 int resource_id;
67 if (ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()-> 72 if (ExtensionsBrowserClient::Get()
68 IsComponentExtensionResource( 73 ->GetComponentExtensionResourceManager()
Devlin 2014/08/04 18:33:25 nit: I'm sure that this is just git cl format, but
Mark Dittmer 2014/08/05 20:33:19 Done.
69 script_file->extension_root(), script_file->relative_path(), 74 ->IsComponentExtensionResource(script_file->extension_root(),
70 &resource_id)) { 75 script_file->relative_path(),
76 &resource_id)) {
71 const ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 77 const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
72 content = rb.GetRawDataResource(resource_id).as_string(); 78 content = rb.GetRawDataResource(resource_id).as_string();
73 } else { 79 } else {
74 LOG(WARNING) << "Failed to get file path to " 80 LOG(WARNING) << "Failed to get file path to "
75 << script_file->relative_path().value() << " from " 81 << script_file->relative_path().value() << " from "
76 << script_file->extension_root().value(); 82 << script_file->extension_root().value();
77 return false; 83 return false;
78 } 84 }
79 } else { 85 } else {
80 if (!base::ReadFileToString(path, &content)) { 86 if (!base::ReadFileToString(path, &content)) {
(...skipping 27 matching lines...) Expand all
108 if (index == 0) { 114 if (index == 0) {
109 script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark))); 115 script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark)));
110 } else { 116 } else {
111 script_file->set_content(content); 117 script_file->set_content(content);
112 } 118 }
113 119
114 return true; 120 return true;
115 } 121 }
116 122
117 SubstitutionMap* GetLocalizationMessages(const ExtensionsInfo& extensions_info, 123 SubstitutionMap* GetLocalizationMessages(const ExtensionsInfo& extensions_info,
118 const std::string& extension_id) { 124 const ExtensionId& extension_id) {
119 ExtensionsInfo::const_iterator iter = extensions_info.find(extension_id); 125 ExtensionsInfo::const_iterator iter = extensions_info.find(extension_id);
120 if (iter == extensions_info.end()) 126 if (iter == extensions_info.end())
121 return NULL; 127 return NULL;
122 return file_util::LoadMessageBundleSubstitutionMap(iter->second.first, 128 return file_util::LoadMessageBundleSubstitutionMap(
123 extension_id, 129 iter->second.first, extension_id, iter->second.second);
124 iter->second.second);
125 } 130 }
126 131
127 void LoadUserScripts(UserScriptList* user_scripts, 132 void LoadUserScripts(std::set<UserScript>* user_scripts,
128 const ExtensionsInfo& extensions_info, 133 const ExtensionsInfo& extensions_info,
129 const std::set<std::string>& new_extensions, 134 const std::set<UserScript>& added_scripts,
130 ContentVerifier* verifier) { 135 ContentVerifier* verifier) {
131 for (size_t i = 0; i < user_scripts->size(); ++i) { 136 for (std::set<UserScript>::iterator script = user_scripts->begin();
132 UserScript& script = user_scripts->at(i); 137 script != user_scripts->end();
133 if (new_extensions.count(script.extension_id()) == 0) 138 ++script) {
139 if (added_scripts.count(*script) == 0)
134 continue; 140 continue;
135 scoped_ptr<SubstitutionMap> localization_messages( 141 scoped_ptr<SubstitutionMap> localization_messages(
136 GetLocalizationMessages(extensions_info, script.extension_id())); 142 GetLocalizationMessages(extensions_info, script->extension_id()));
137 for (size_t k = 0; k < script.js_scripts().size(); ++k) { 143 for (size_t k = 0; k < script->js_scripts().size(); ++k) {
138 UserScript::File& script_file = script.js_scripts()[k]; 144 UserScript::File& script_file =
145 const_cast<UserScript::File&>(script->js_scripts()[k]);
Devlin 2014/08/04 18:33:25 Why do we need the const cast?
Mark Dittmer 2014/08/05 20:33:19 std::set const'ifies its iterators because it's co
Devlin 2014/08/05 21:47:26 Ah. Right you are. Unfortunately, any kind of mo
Mark Dittmer 2014/08/06 15:32:24 I don't think we can get away with added/removed a
139 if (script_file.GetContent().empty()) 146 if (script_file.GetContent().empty())
140 LoadScriptContent( 147 LoadScriptContent(script->extension_id(), &script_file, NULL, verifier);
141 script.extension_id(), &script_file, NULL, verifier);
142 } 148 }
143 for (size_t k = 0; k < script.css_scripts().size(); ++k) { 149 for (size_t k = 0; k < script->css_scripts().size(); ++k) {
144 UserScript::File& script_file = script.css_scripts()[k]; 150 UserScript::File& script_file =
151 const_cast<UserScript::File&>(script->css_scripts()[k]);
145 if (script_file.GetContent().empty()) 152 if (script_file.GetContent().empty())
146 LoadScriptContent(script.extension_id(), 153 LoadScriptContent(script->extension_id(),
147 &script_file, 154 &script_file,
148 localization_messages.get(), 155 localization_messages.get(),
149 verifier); 156 verifier);
150 } 157 }
151 } 158 }
152 } 159 }
153 160
154 // Pickle user scripts and return pointer to the shared memory. 161 // Pickle user scripts and return pointer to the shared memory.
155 scoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) { 162 scoped_ptr<base::SharedMemory> Serialize(const std::set<UserScript>& scripts) {
156 Pickle pickle; 163 Pickle pickle;
157 pickle.WriteUInt64(scripts.size()); 164 pickle.WriteUInt64(scripts.size());
158 for (size_t i = 0; i < scripts.size(); i++) { 165 for (std::set<UserScript>::const_iterator script = scripts.begin();
159 const UserScript& script = scripts[i]; 166 script != scripts.end();
167 ++script) {
160 // TODO(aa): This can be replaced by sending content script metadata to 168 // TODO(aa): This can be replaced by sending content script metadata to
161 // renderers along with other extension data in ExtensionMsg_Loaded. 169 // renderers along with other extension data in ExtensionMsg_Loaded.
162 // See crbug.com/70516. 170 // See crbug.com/70516.
163 script.Pickle(&pickle); 171 script->Pickle(&pickle);
164 // Write scripts as 'data' so that we can read it out in the slave without 172 // Write scripts as 'data' so that we can read it out in the slave without
165 // allocating a new string. 173 // allocating a new string.
166 for (size_t j = 0; j < script.js_scripts().size(); j++) { 174 for (size_t j = 0; j < script->js_scripts().size(); j++) {
167 base::StringPiece contents = script.js_scripts()[j].GetContent(); 175 base::StringPiece contents = script->js_scripts()[j].GetContent();
168 pickle.WriteData(contents.data(), contents.length()); 176 pickle.WriteData(contents.data(), contents.length());
169 } 177 }
170 for (size_t j = 0; j < script.css_scripts().size(); j++) { 178 for (size_t j = 0; j < script->css_scripts().size(); j++) {
171 base::StringPiece contents = script.css_scripts()[j].GetContent(); 179 base::StringPiece contents = script->css_scripts()[j].GetContent();
172 pickle.WriteData(contents.data(), contents.length()); 180 pickle.WriteData(contents.data(), contents.length());
173 } 181 }
174 } 182 }
175 183
176 // Create the shared memory object. 184 // Create the shared memory object.
177 base::SharedMemory shared_memory; 185 base::SharedMemory shared_memory;
178 186
179 base::SharedMemoryCreateOptions options; 187 base::SharedMemoryCreateOptions options;
180 options.size = pickle.size(); 188 options.size = pickle.size();
181 options.share_read_only = true; 189 options.share_read_only = true;
182 if (!shared_memory.Create(options)) 190 if (!shared_memory.Create(options))
183 return scoped_ptr<base::SharedMemory>(); 191 return scoped_ptr<base::SharedMemory>();
184 192
185 if (!shared_memory.Map(pickle.size())) 193 if (!shared_memory.Map(pickle.size()))
186 return scoped_ptr<base::SharedMemory>(); 194 return scoped_ptr<base::SharedMemory>();
187 195
188 // Copy the pickle to shared memory. 196 // Copy the pickle to shared memory.
189 memcpy(shared_memory.memory(), pickle.data(), pickle.size()); 197 memcpy(shared_memory.memory(), pickle.data(), pickle.size());
190 198
191 base::SharedMemoryHandle readonly_handle; 199 base::SharedMemoryHandle readonly_handle;
192 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(), 200 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(),
193 &readonly_handle)) 201 &readonly_handle))
194 return scoped_ptr<base::SharedMemory>(); 202 return scoped_ptr<base::SharedMemory>();
195 203
196 return make_scoped_ptr(new base::SharedMemory(readonly_handle, 204 return make_scoped_ptr(new base::SharedMemory(readonly_handle,
197 /*read_only=*/true)); 205 /*read_only=*/true));
198 } 206 }
199 207
200 void LoadScriptsOnFileThread(scoped_ptr<UserScriptList> user_scripts, 208 void LoadScriptsOnFileThread(scoped_ptr<std::set<UserScript> > user_scripts,
201 const ExtensionsInfo& extensions_info, 209 const ExtensionsInfo& extensions_info,
202 const std::set<std::string>& new_extensions, 210 const std::set<UserScript>& added_scripts,
203 scoped_refptr<ContentVerifier> verifier, 211 scoped_refptr<ContentVerifier> verifier,
204 LoadScriptsCallback callback) { 212 LoadScriptsCallback callback) {
205 DCHECK(user_scripts.get()); 213 DCHECK(user_scripts.get());
206 LoadUserScripts( 214 LoadUserScripts(user_scripts.get(), extensions_info, added_scripts, verifier);
207 user_scripts.get(), extensions_info, new_extensions, verifier);
208 scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts); 215 scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts);
209 BrowserThread::PostTask( 216 BrowserThread::PostTask(
210 BrowserThread::UI, 217 BrowserThread::UI,
211 FROM_HERE, 218 FROM_HERE,
212 base::Bind(callback, 219 base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory)));
213 base::Passed(&user_scripts),
214 base::Passed(&memory)));
215 } 220 }
216 221
217 // Helper function to parse greasesmonkey headers 222 // Helper function to parse greasesmonkey headers
218 bool GetDeclarationValue(const base::StringPiece& line, 223 bool GetDeclarationValue(const base::StringPiece& line,
219 const base::StringPiece& prefix, 224 const base::StringPiece& prefix,
220 std::string* value) { 225 std::string* value) {
221 base::StringPiece::size_type index = line.find(prefix); 226 base::StringPiece::size_type index = line.find(prefix);
222 if (index == base::StringPiece::npos) 227 if (index == base::StringPiece::npos)
223 return false; 228 return false;
224 229
225 std::string temp(line.data() + index + prefix.length(), 230 std::string temp(line.data() + index + prefix.length(),
226 line.length() - index - prefix.length()); 231 line.length() - index - prefix.length());
227 232
228 if (temp.empty() || !IsWhitespace(temp[0])) 233 if (temp.empty() || !IsWhitespace(temp[0]))
229 return false; 234 return false;
230 235
231 base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value); 236 base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value);
232 return true; 237 return true;
233 } 238 }
234 239
235 } // namespace 240 } // namespace
236 241
237 // static 242 // static
238 bool UserScriptMaster::ParseMetadataHeader( 243 bool UserScriptLoader::ParseMetadataHeader(const base::StringPiece& script_text,
239 const base::StringPiece& script_text, UserScript* script) { 244 UserScript* script) {
240 // http://wiki.greasespot.net/Metadata_block 245 // http://wiki.greasespot.net/Metadata_block
241 base::StringPiece line; 246 base::StringPiece line;
242 size_t line_start = 0; 247 size_t line_start = 0;
243 size_t line_end = line_start; 248 size_t line_end = line_start;
244 bool in_metadata = false; 249 bool in_metadata = false;
245 250
246 static const base::StringPiece kUserScriptBegin("// ==UserScript=="); 251 static const base::StringPiece kUserScriptBegin("// ==UserScript==");
247 static const base::StringPiece kUserScriptEng("// ==/UserScript=="); 252 static const base::StringPiece kUserScriptEng("// ==/UserScript==");
248 static const base::StringPiece kNamespaceDeclaration("// @namespace"); 253 static const base::StringPiece kNamespaceDeclaration("// @namespace");
249 static const base::StringPiece kNameDeclaration("// @name"); 254 static const base::StringPiece kNameDeclaration("// @name");
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
323 328
324 // If no patterns were specified, default to @include *. This is what 329 // If no patterns were specified, default to @include *. This is what
325 // Greasemonkey does. 330 // Greasemonkey does.
326 if (script->globs().empty() && script->url_patterns().is_empty()) 331 if (script->globs().empty() && script->url_patterns().is_empty())
327 script->add_glob("*"); 332 script->add_glob("*");
328 333
329 return true; 334 return true;
330 } 335 }
331 336
332 // static 337 // static
333 void UserScriptMaster::LoadScriptsForTest(UserScriptList* user_scripts) { 338 void UserScriptLoader::LoadScriptsForTest(std::set<UserScript>* user_scripts) {
334 ExtensionsInfo info; 339 ExtensionsInfo info;
335 std::set<std::string> new_extensions; 340 std::set<UserScript> added_scripts;
336 for (UserScriptList::const_iterator iter = user_scripts->begin(); 341 added_scripts.insert(user_scripts->begin(), user_scripts->end());
337 iter != user_scripts->end();
338 ++iter) {
339 new_extensions.insert(iter->extension_id());
340 }
341 LoadUserScripts( 342 LoadUserScripts(
342 user_scripts, info, new_extensions, NULL /* no verifier for testing */); 343 user_scripts, info, added_scripts, NULL /* no verifier for testing */);
343 } 344 }
344 345
345 UserScriptMaster::UserScriptMaster(Profile* profile) 346 UserScriptLoader::UserScriptLoader(Profile* profile,
346 : user_scripts_(new UserScriptList()), 347 ExtensionId owner_extension_id,
347 extensions_service_ready_(false), 348 bool listen_for_extension_system_loaded)
349 : user_scripts_(new std::set<UserScript>()),
350 extension_system_ready_(false),
348 pending_load_(false), 351 pending_load_(false),
349 profile_(profile), 352 profile_(profile),
350 extension_registry_observer_(this), 353 owner_extension_id_(owner_extension_id),
351 weak_factory_(this) { 354 weak_factory_(this) {
352 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); 355 if (listen_for_extension_system_loaded) {
356 extension_system_ready_ = false;
Devlin 2014/08/04 18:33:25 This was already set in the constructor.
Mark Dittmer 2014/08/05 20:33:19 True. I was thinking that the code looks more expl
Devlin 2014/08/05 21:47:26 Remove this line. It's pretty common practice to
Mark Dittmer 2014/08/06 15:32:24 Done.
357 ExtensionSystem::Get(profile_)->ready().Post(
358 FROM_HERE,
359 base::Bind(&UserScriptLoader::OnExtensionSystemReady,
360 weak_factory_.GetWeakPtr()));
361 } else {
362 extension_system_ready_ = true;
363 }
353 registrar_.Add(this, 364 registrar_.Add(this,
354 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 365 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
355 content::Source<Profile>(profile_));
356 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
357 content::NotificationService::AllBrowserContextsAndSources()); 366 content::NotificationService::AllBrowserContextsAndSources());
358 } 367 }
359 368
360 UserScriptMaster::~UserScriptMaster() { 369 UserScriptLoader::~UserScriptLoader() {
361 } 370 }
362 371
363 void UserScriptMaster::OnScriptsLoaded( 372 void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) {
364 scoped_ptr<UserScriptList> user_scripts, 373 for (std::set<UserScript>::const_iterator it = scripts.begin();
374 it != scripts.end();
375 ++it) {
376 if (removed_scripts_.count(*it) > 0)
Devlin 2014/08/04 18:33:25 We don't need these ifs - we won't remove a nonexi
Mark Dittmer 2014/08/05 20:33:19 Done.
377 removed_scripts_.erase(*it);
378 if (added_scripts_.count(*it) == 0)
379 added_scripts_.insert(*it);
380 }
381 AttemptLoad();
382 }
383
384 void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) {
385 for (std::set<UserScript>::const_iterator it = scripts.begin();
386 it != scripts.end();
387 ++it) {
388 if (added_scripts_.count(*it) > 0)
Devlin 2014/08/04 18:33:25 ditto
Mark Dittmer 2014/08/05 20:33:19 Done.
389 added_scripts_.erase(*it);
390 if (removed_scripts_.count(*it) == 0)
391 removed_scripts_.insert(*it);
392 }
393 AttemptLoad();
394 }
395
396 void UserScriptLoader::ClearScripts() {
397 clear_scripts_ = true;
398 added_scripts_.clear();
399 removed_scripts_.clear();
400 AttemptLoad();
401 }
402
403 void UserScriptLoader::OnExtensionSystemReady() {
404 extension_system_ready_ = true;
405 AttemptLoad();
406 }
407
408 void UserScriptLoader::Observe(int type,
409 const content::NotificationSource& source,
410 const content::NotificationDetails& details) {
411 DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED);
412 content::RenderProcessHost* process =
413 content::Source<content::RenderProcessHost>(source).ptr();
414 Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
415 if (!profile_->IsSameProfile(profile))
416 return;
417 if (ScriptsReady()) {
418 SendUpdate(process,
419 GetSharedMemory(),
420 std::set<ExtensionId>()); // Include all extensions.
421 }
422 AttemptLoad();
423 }
424
425 void UserScriptLoader::AttemptLoad() {
Devlin 2014/08/04 18:33:25 Shouldn't we also check extension_system_ready_?
Mark Dittmer 2014/08/05 20:33:19 Yes! Good catch.
426 if (is_loading())
427 pending_load_ = true;
428 else
429 StartLoad();
430 }
431
432 void UserScriptLoader::StartLoad() {
433 DCHECK_CURRENTLY_ON(BrowserThread::UI);
434 DCHECK(!is_loading());
435
436 // If scripts were marked for clearing before adding+removal, then clear them.
Devlin 2014/08/04 18:33:25 adding and removing
Mark Dittmer 2014/08/05 20:33:19 Done.
437 if (clear_scripts_)
438 user_scripts_->clear();
439
440 // Remove scripts marked for removal.
441 for (std::set<UserScript>::iterator it = removed_scripts_.begin();
Devlin 2014/08/04 18:33:25 Is there ever a case in which we need to remove sc
Mark Dittmer 2014/08/05 20:33:19 No. Good point.
442 it != removed_scripts_.end();
443 ++it) {
444 if (user_scripts_->count(*it) != 0)
Devlin 2014/08/04 18:33:25 again, no reason for the if
Mark Dittmer 2014/08/05 20:33:19 Done.
445 user_scripts_->erase(it);
446 }
447
448 // Add scripts marked for addition.
449 user_scripts_->insert(added_scripts_.begin(), added_scripts_.end());
450
451 // Recompute |changed_extensions_| for OnScriptsLoaded, which will use it in
452 // its IPC message. this must be done before we clear |added_scripts_| and
453 // |removed_scripts_| below.
454 ComputeChangedExtensions();
455
456 // Update |extensions_info_| to contain info from every extension in
457 // |changed_extensions_| before passing it to LoadScriptsOnFileThread.
458 UpdateExtensionsInfo();
459
460 BrowserThread::PostTask(
461 BrowserThread::FILE,
462 FROM_HERE,
463 base::Bind(&LoadScriptsOnFileThread,
464 base::Passed(&user_scripts_),
465 extensions_info_,
466 added_scripts_,
467 make_scoped_refptr(
468 ExtensionSystem::Get(profile_)->content_verifier()),
469 base::Bind(&UserScriptLoader::OnScriptsLoaded,
470 weak_factory_.GetWeakPtr())));
471
472 clear_scripts_ = false;
473 added_scripts_.clear();
474 removed_scripts_.clear();
475 user_scripts_.reset(NULL);
476 }
477
478 void UserScriptLoader::OnScriptsLoaded(
479 scoped_ptr<std::set<UserScript> > user_scripts,
365 scoped_ptr<base::SharedMemory> shared_memory) { 480 scoped_ptr<base::SharedMemory> shared_memory) {
366 user_scripts_.reset(user_scripts.release()); 481 user_scripts_.reset(user_scripts.release());
367 if (pending_load_) { 482 if (pending_load_) {
368 // While we were loading, there were further changes. Don't bother 483 // While we were loading, there were further changes. Don't bother
369 // notifying about these scripts and instead just immediately reload. 484 // notifying about these scripts and instead just immediately reload.
370 pending_load_ = false; 485 pending_load_ = false;
371 StartLoad(); 486 StartLoad();
372 return; 487 return;
373 } 488 }
374 489
375 if (shared_memory.get() == NULL) { 490 if (shared_memory.get() == NULL) {
376 // This can happen if we run out of file descriptors. In that case, we 491 // This can happen if we run out of file descriptors. In that case, we
377 // have a choice between silently omitting all user scripts for new tabs, 492 // have a choice between silently omitting all user scripts for new tabs,
378 // by nulling out shared_memory_, or only silently omitting new ones by 493 // by nulling out shared_memory_, or only silently omitting new ones by
379 // leaving the existing object in place. The second seems less bad, even 494 // leaving the existing object in place. The second seems less bad, even
380 // though it removes the possibility that freeing the shared memory block 495 // though it removes the possibility that freeing the shared memory block
381 // would open up enough FDs for long enough for a retry to succeed. 496 // would open up enough FDs for long enough for a retry to succeed.
382 497
383 // Pretend the extension change didn't happen. 498 // Pretend the extension change didn't happen.
384 return; 499 return;
385 } 500 }
386 501
387 // We've got scripts ready to go. 502 // We've got scripts ready to go.
388 shared_memory_.reset(shared_memory.release()); 503 shared_memory_.reset(shared_memory.release());
389 504
390 for (content::RenderProcessHost::iterator i( 505 for (content::RenderProcessHost::iterator i(
391 content::RenderProcessHost::AllHostsIterator()); 506 content::RenderProcessHost::AllHostsIterator());
392 !i.IsAtEnd(); i.Advance()) { 507 !i.IsAtEnd();
393 SendUpdate(i.GetCurrentValue(), 508 i.Advance()) {
394 shared_memory_.get(), 509 SendUpdate(i.GetCurrentValue(), shared_memory_.get(), changed_extensions_);
395 changed_extensions_);
396 } 510 }
397 changed_extensions_.clear();
398 511
399 content::NotificationService::current()->Notify( 512 content::NotificationService::current()->Notify(
400 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED, 513 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
401 content::Source<Profile>(profile_), 514 content::Source<Profile>(profile_),
402 content::Details<base::SharedMemory>(shared_memory_.get())); 515 content::Details<base::SharedMemory>(shared_memory_.get()));
403 } 516 }
404 517
405 void UserScriptMaster::OnExtensionLoaded( 518 void UserScriptLoader::SendUpdate(
406 content::BrowserContext* browser_context,
407 const Extension* extension) {
408 added_extensions_.insert(extension->id());
409 removed_extensions_.erase(extension->id());
410 extensions_info_[extension->id()] =
411 ExtensionSet::ExtensionPathAndDefaultLocale(
412 extension->path(), LocaleInfo::GetDefaultLocale(extension));
413 if (extensions_service_ready_) {
414 changed_extensions_.insert(extension->id());
415 if (is_loading())
416 pending_load_ = true;
417 else
418 StartLoad();
419 }
420 }
421
422 void UserScriptMaster::OnExtensionUnloaded(
423 content::BrowserContext* browser_context,
424 const Extension* extension,
425 UnloadedExtensionInfo::Reason reason) {
426 removed_extensions_.insert(extension->id());
427 added_extensions_.erase(extension->id());
428 // Remove any content scripts.
429 extensions_info_.erase(extension->id());
430 changed_extensions_.insert(extension->id());
431 if (is_loading())
432 pending_load_ = true;
433 else
434 StartLoad();
435 }
436
437 void UserScriptMaster::Observe(int type,
438 const content::NotificationSource& source,
439 const content::NotificationDetails& details) {
440 bool should_start_load = false;
441 switch (type) {
442 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED:
443 extensions_service_ready_ = true;
444 should_start_load = true;
445 break;
446 case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
447 content::RenderProcessHost* process =
448 content::Source<content::RenderProcessHost>(source).ptr();
449 Profile* profile = Profile::FromBrowserContext(
450 process->GetBrowserContext());
451 if (!profile_->IsSameProfile(profile))
452 return;
453 if (ScriptsReady()) {
454 SendUpdate(process,
455 GetSharedMemory(),
456 std::set<std::string>()); // Include all extensions.
457 }
458 break;
459 }
460 default:
461 DCHECK(false);
462 }
463
464 if (should_start_load) {
465 if (is_loading())
466 pending_load_ = true;
467 else
468 StartLoad();
469 }
470 }
471
472 void UserScriptMaster::StartLoad() {
473 DCHECK_CURRENTLY_ON(BrowserThread::UI);
474 DCHECK(!is_loading());
475
476 // Remove any user scripts belonging to any extension that was updated or
477 // removed.
478 for (UserScriptList::iterator iter = user_scripts_->begin();
479 iter != user_scripts_->end();) {
480 if (removed_extensions_.count(iter->extension_id()) > 0 ||
481 added_extensions_.count(iter->extension_id()) > 0) {
482 iter = user_scripts_->erase(iter);
483 } else {
484 ++iter;
485 }
486 }
487
488 // Add any content scripts for extensions that were recently loaded.
489 const ExtensionSet& enabled_extensions =
490 ExtensionRegistry::Get(profile_)->enabled_extensions();
491 for (std::set<std::string>::const_iterator iter = added_extensions_.begin();
492 iter != added_extensions_.end(); ++iter) {
493 const Extension* extension = enabled_extensions.GetByID(*iter);
494 if (!extension)
495 continue;
496 bool incognito_enabled =
497 util::IsIncognitoEnabled(extension->id(), profile_);
498 const UserScriptList& scripts =
499 ContentScriptsInfo::GetContentScripts(extension);
500 for (UserScriptList::const_iterator script = scripts.begin();
501 script != scripts.end();
502 ++script) {
503 user_scripts_->push_back(*script);
504 user_scripts_->back().set_incognito_enabled(incognito_enabled);
505 }
506 }
507
508 BrowserThread::PostTask(
509 BrowserThread::FILE,
510 FROM_HERE,
511 base::Bind(&LoadScriptsOnFileThread,
512 base::Passed(&user_scripts_),
513 extensions_info_,
514 added_extensions_,
515 make_scoped_refptr(
516 ExtensionSystem::Get(profile_)->content_verifier()),
517 base::Bind(&UserScriptMaster::OnScriptsLoaded,
518 weak_factory_.GetWeakPtr())));
519 added_extensions_.clear();
520 removed_extensions_.clear();
521 user_scripts_.reset(NULL);
522 }
523
524 void UserScriptMaster::SendUpdate(
525 content::RenderProcessHost* process, 519 content::RenderProcessHost* process,
526 base::SharedMemory* shared_memory, 520 base::SharedMemory* shared_memory,
527 const std::set<std::string>& changed_extensions) { 521 const std::set<ExtensionId>& changed_extensions) {
528 // Don't allow injection of content scripts into <webview>. 522 // Don't allow injection of content scripts into <webview>.
529 if (process->IsIsolatedGuest()) 523 if (process->IsIsolatedGuest())
530 return; 524 return;
531 525
532 Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext()); 526 Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
533 // Make sure we only send user scripts to processes in our profile. 527 // Make sure we only send user scripts to processes in our profile.
534 if (!profile_->IsSameProfile(profile)) 528 if (!profile_->IsSameProfile(profile))
535 return; 529 return;
536 530
537 // If the process is being started asynchronously, early return. We'll end up 531 // If the process is being started asynchronously, early return. We'll end up
538 // calling InitUserScripts when it's created which will call this again. 532 // calling InitUserScripts when it's created which will call this again.
539 base::ProcessHandle handle = process->GetHandle(); 533 base::ProcessHandle handle = process->GetHandle();
540 if (!handle) 534 if (!handle)
541 return; 535 return;
542 536
543 base::SharedMemoryHandle handle_for_process; 537 base::SharedMemoryHandle handle_for_process;
544 if (!shared_memory->ShareToProcess(handle, &handle_for_process)) 538 if (!shared_memory->ShareToProcess(handle, &handle_for_process))
545 return; // This can legitimately fail if the renderer asserts at startup. 539 return; // This can legitimately fail if the renderer asserts at startup.
546 540
547 if (base::SharedMemory::IsHandleValid(handle_for_process)) { 541 if (base::SharedMemory::IsHandleValid(handle_for_process)) {
548 process->Send(new ExtensionMsg_UpdateUserScripts( 542 process->Send(new ExtensionMsg_UpdateUserScripts(
549 handle_for_process, "" /* owner */, changed_extensions)); 543 handle_for_process, "" /* owner */, changed_extensions));
550 } 544 }
551 } 545 }
552 546
547 void UserScriptLoader::ComputeChangedExtensions() {
548 changed_extensions_.clear();
549 for (std::set<UserScript>::const_iterator it = removed_scripts_.begin();
550 it != removed_scripts_.end();
551 ++it) {
552 if (changed_extensions_.count(it->extension_id()) == 0)
Devlin 2014/08/04 18:33:25 no ifs
Mark Dittmer 2014/08/05 20:33:19 Done.
553 changed_extensions_.insert(it->extension_id());
554 }
555 for (std::set<UserScript>::const_iterator it = added_scripts_.begin();
556 it != added_scripts_.end();
557 ++it) {
558 if (changed_extensions_.count(it->extension_id()) == 0)
559 changed_extensions_.insert(it->extension_id());
560 }
561 }
562
563 void UserScriptLoader::UpdateExtensionsInfo() {
564 for (std::set<ExtensionId>::const_iterator it = changed_extensions_.begin();
565 it != changed_extensions_.end();
566 ++it) {
567 if (extensions_info_.find(*it) == extensions_info_.end()) {
568 const Extension* extension =
569 ExtensionRegistry::Get(profile_)
570 ->GetExtensionById(*it, ExtensionRegistry::EVERYTHING);
571 extensions_info_[*it] = ExtensionSet::ExtensionPathAndDefaultLocale(
572 extension->path(), LocaleInfo::GetDefaultLocale(extension));
573 }
574 }
575 }
576
553 } // namespace extensions 577 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698