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

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: Implement changes from code review comments 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()->GetComponentExtensionResourceManager()->
68 IsComponentExtensionResource( 73 IsComponentExtensionResource(script_file->extension_root(),
69 script_file->extension_root(), script_file->relative_path(), 74 script_file->relative_path(),
70 &resource_id)) { 75 &resource_id)) {
71 const ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 76 const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
72 content = rb.GetRawDataResource(resource_id).as_string(); 77 content = rb.GetRawDataResource(resource_id).as_string();
73 } else { 78 } else {
74 LOG(WARNING) << "Failed to get file path to " 79 LOG(WARNING) << "Failed to get file path to "
75 << script_file->relative_path().value() << " from " 80 << script_file->relative_path().value() << " from "
76 << script_file->extension_root().value(); 81 << script_file->extension_root().value();
77 return false; 82 return false;
78 } 83 }
79 } else { 84 } else {
80 if (!base::ReadFileToString(path, &content)) { 85 if (!base::ReadFileToString(path, &content)) {
(...skipping 27 matching lines...) Expand all
108 if (index == 0) { 113 if (index == 0) {
109 script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark))); 114 script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark)));
110 } else { 115 } else {
111 script_file->set_content(content); 116 script_file->set_content(content);
112 } 117 }
113 118
114 return true; 119 return true;
115 } 120 }
116 121
117 SubstitutionMap* GetLocalizationMessages(const ExtensionsInfo& extensions_info, 122 SubstitutionMap* GetLocalizationMessages(const ExtensionsInfo& extensions_info,
118 const std::string& extension_id) { 123 const ExtensionId& extension_id) {
119 ExtensionsInfo::const_iterator iter = extensions_info.find(extension_id); 124 ExtensionsInfo::const_iterator iter = extensions_info.find(extension_id);
120 if (iter == extensions_info.end()) 125 if (iter == extensions_info.end())
121 return NULL; 126 return NULL;
122 return file_util::LoadMessageBundleSubstitutionMap(iter->second.first, 127 return file_util::LoadMessageBundleSubstitutionMap(
123 extension_id, 128 iter->second.first, extension_id, iter->second.second);
124 iter->second.second);
125 } 129 }
126 130
127 void LoadUserScripts(UserScriptList* user_scripts, 131 void LoadUserScripts(std::set<UserScript>* user_scripts,
128 const ExtensionsInfo& extensions_info, 132 const ExtensionsInfo& extensions_info,
129 const std::set<std::string>& new_extensions, 133 const std::set<UserScript>& added_scripts,
130 ContentVerifier* verifier) { 134 ContentVerifier* verifier) {
131 for (size_t i = 0; i < user_scripts->size(); ++i) { 135 for (std::set<UserScript>::iterator script = user_scripts->begin();
132 UserScript& script = user_scripts->at(i); 136 script != user_scripts->end();
133 if (new_extensions.count(script.extension_id()) == 0) 137 ++script) {
138 if (added_scripts.count(*script) == 0)
134 continue; 139 continue;
135 scoped_ptr<SubstitutionMap> localization_messages( 140 scoped_ptr<SubstitutionMap> localization_messages(
136 GetLocalizationMessages(extensions_info, script.extension_id())); 141 GetLocalizationMessages(extensions_info, script->extension_id()));
137 for (size_t k = 0; k < script.js_scripts().size(); ++k) { 142 for (size_t k = 0; k < script->js_scripts().size(); ++k) {
138 UserScript::File& script_file = script.js_scripts()[k]; 143 UserScript::File& script_file =
144 const_cast<UserScript::File&>(script->js_scripts()[k]);
139 if (script_file.GetContent().empty()) 145 if (script_file.GetContent().empty())
140 LoadScriptContent( 146 LoadScriptContent(script->extension_id(), &script_file, NULL, verifier);
141 script.extension_id(), &script_file, NULL, verifier);
142 } 147 }
143 for (size_t k = 0; k < script.css_scripts().size(); ++k) { 148 for (size_t k = 0; k < script->css_scripts().size(); ++k) {
144 UserScript::File& script_file = script.css_scripts()[k]; 149 UserScript::File& script_file =
150 const_cast<UserScript::File&>(script->css_scripts()[k]);
145 if (script_file.GetContent().empty()) 151 if (script_file.GetContent().empty())
146 LoadScriptContent(script.extension_id(), 152 LoadScriptContent(script->extension_id(),
147 &script_file, 153 &script_file,
148 localization_messages.get(), 154 localization_messages.get(),
149 verifier); 155 verifier);
150 } 156 }
151 } 157 }
152 } 158 }
153 159
154 // Pickle user scripts and return pointer to the shared memory. 160 // Pickle user scripts and return pointer to the shared memory.
155 scoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) { 161 scoped_ptr<base::SharedMemory> Serialize(const std::set<UserScript>& scripts) {
156 Pickle pickle; 162 Pickle pickle;
157 pickle.WriteUInt64(scripts.size()); 163 pickle.WriteUInt64(scripts.size());
158 for (size_t i = 0; i < scripts.size(); i++) { 164 for (std::set<UserScript>::const_iterator script = scripts.begin();
159 const UserScript& script = scripts[i]; 165 script != scripts.end();
166 ++script) {
160 // TODO(aa): This can be replaced by sending content script metadata to 167 // TODO(aa): This can be replaced by sending content script metadata to
161 // renderers along with other extension data in ExtensionMsg_Loaded. 168 // renderers along with other extension data in ExtensionMsg_Loaded.
162 // See crbug.com/70516. 169 // See crbug.com/70516.
163 script.Pickle(&pickle); 170 script->Pickle(&pickle);
164 // Write scripts as 'data' so that we can read it out in the slave without 171 // Write scripts as 'data' so that we can read it out in the slave without
165 // allocating a new string. 172 // allocating a new string.
166 for (size_t j = 0; j < script.js_scripts().size(); j++) { 173 for (size_t j = 0; j < script->js_scripts().size(); j++) {
167 base::StringPiece contents = script.js_scripts()[j].GetContent(); 174 base::StringPiece contents = script->js_scripts()[j].GetContent();
168 pickle.WriteData(contents.data(), contents.length()); 175 pickle.WriteData(contents.data(), contents.length());
169 } 176 }
170 for (size_t j = 0; j < script.css_scripts().size(); j++) { 177 for (size_t j = 0; j < script->css_scripts().size(); j++) {
171 base::StringPiece contents = script.css_scripts()[j].GetContent(); 178 base::StringPiece contents = script->css_scripts()[j].GetContent();
172 pickle.WriteData(contents.data(), contents.length()); 179 pickle.WriteData(contents.data(), contents.length());
173 } 180 }
174 } 181 }
175 182
176 // Create the shared memory object. 183 // Create the shared memory object.
177 base::SharedMemory shared_memory; 184 base::SharedMemory shared_memory;
178 185
179 base::SharedMemoryCreateOptions options; 186 base::SharedMemoryCreateOptions options;
180 options.size = pickle.size(); 187 options.size = pickle.size();
181 options.share_read_only = true; 188 options.share_read_only = true;
182 if (!shared_memory.Create(options)) 189 if (!shared_memory.Create(options))
183 return scoped_ptr<base::SharedMemory>(); 190 return scoped_ptr<base::SharedMemory>();
184 191
185 if (!shared_memory.Map(pickle.size())) 192 if (!shared_memory.Map(pickle.size()))
186 return scoped_ptr<base::SharedMemory>(); 193 return scoped_ptr<base::SharedMemory>();
187 194
188 // Copy the pickle to shared memory. 195 // Copy the pickle to shared memory.
189 memcpy(shared_memory.memory(), pickle.data(), pickle.size()); 196 memcpy(shared_memory.memory(), pickle.data(), pickle.size());
190 197
191 base::SharedMemoryHandle readonly_handle; 198 base::SharedMemoryHandle readonly_handle;
192 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(), 199 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(),
193 &readonly_handle)) 200 &readonly_handle))
194 return scoped_ptr<base::SharedMemory>(); 201 return scoped_ptr<base::SharedMemory>();
195 202
196 return make_scoped_ptr(new base::SharedMemory(readonly_handle, 203 return make_scoped_ptr(new base::SharedMemory(readonly_handle,
197 /*read_only=*/true)); 204 /*read_only=*/true));
198 } 205 }
199 206
200 void LoadScriptsOnFileThread(scoped_ptr<UserScriptList> user_scripts, 207 void LoadScriptsOnFileThread(scoped_ptr<std::set<UserScript> > user_scripts,
201 const ExtensionsInfo& extensions_info, 208 const ExtensionsInfo& extensions_info,
202 const std::set<std::string>& new_extensions, 209 const std::set<UserScript>& added_scripts,
203 scoped_refptr<ContentVerifier> verifier, 210 scoped_refptr<ContentVerifier> verifier,
204 LoadScriptsCallback callback) { 211 LoadScriptsCallback callback) {
205 DCHECK(user_scripts.get()); 212 DCHECK(user_scripts.get());
206 LoadUserScripts( 213 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); 214 scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts);
209 BrowserThread::PostTask( 215 BrowserThread::PostTask(
210 BrowserThread::UI, 216 BrowserThread::UI,
211 FROM_HERE, 217 FROM_HERE,
212 base::Bind(callback, 218 base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory)));
213 base::Passed(&user_scripts),
214 base::Passed(&memory)));
215 } 219 }
216 220
217 // Helper function to parse greasesmonkey headers 221 // Helper function to parse greasesmonkey headers
218 bool GetDeclarationValue(const base::StringPiece& line, 222 bool GetDeclarationValue(const base::StringPiece& line,
219 const base::StringPiece& prefix, 223 const base::StringPiece& prefix,
220 std::string* value) { 224 std::string* value) {
221 base::StringPiece::size_type index = line.find(prefix); 225 base::StringPiece::size_type index = line.find(prefix);
222 if (index == base::StringPiece::npos) 226 if (index == base::StringPiece::npos)
223 return false; 227 return false;
224 228
225 std::string temp(line.data() + index + prefix.length(), 229 std::string temp(line.data() + index + prefix.length(),
226 line.length() - index - prefix.length()); 230 line.length() - index - prefix.length());
227 231
228 if (temp.empty() || !IsWhitespace(temp[0])) 232 if (temp.empty() || !IsWhitespace(temp[0]))
229 return false; 233 return false;
230 234
231 base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value); 235 base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value);
232 return true; 236 return true;
233 } 237 }
234 238
235 } // namespace 239 } // namespace
236 240
237 // static 241 // static
238 bool UserScriptMaster::ParseMetadataHeader( 242 bool UserScriptLoader::ParseMetadataHeader(const base::StringPiece& script_text,
239 const base::StringPiece& script_text, UserScript* script) { 243 UserScript* script) {
240 // http://wiki.greasespot.net/Metadata_block 244 // http://wiki.greasespot.net/Metadata_block
241 base::StringPiece line; 245 base::StringPiece line;
242 size_t line_start = 0; 246 size_t line_start = 0;
243 size_t line_end = line_start; 247 size_t line_end = line_start;
244 bool in_metadata = false; 248 bool in_metadata = false;
245 249
246 static const base::StringPiece kUserScriptBegin("// ==UserScript=="); 250 static const base::StringPiece kUserScriptBegin("// ==UserScript==");
247 static const base::StringPiece kUserScriptEng("// ==/UserScript=="); 251 static const base::StringPiece kUserScriptEng("// ==/UserScript==");
248 static const base::StringPiece kNamespaceDeclaration("// @namespace"); 252 static const base::StringPiece kNamespaceDeclaration("// @namespace");
249 static const base::StringPiece kNameDeclaration("// @name"); 253 static const base::StringPiece kNameDeclaration("// @name");
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
323 327
324 // If no patterns were specified, default to @include *. This is what 328 // If no patterns were specified, default to @include *. This is what
325 // Greasemonkey does. 329 // Greasemonkey does.
326 if (script->globs().empty() && script->url_patterns().is_empty()) 330 if (script->globs().empty() && script->url_patterns().is_empty())
327 script->add_glob("*"); 331 script->add_glob("*");
328 332
329 return true; 333 return true;
330 } 334 }
331 335
332 // static 336 // static
333 void UserScriptMaster::LoadScriptsForTest(UserScriptList* user_scripts) { 337 void UserScriptLoader::LoadScriptsForTest(std::set<UserScript>* user_scripts) {
334 ExtensionsInfo info; 338 ExtensionsInfo info;
335 std::set<std::string> new_extensions; 339 std::set<UserScript> added_scripts;
336 for (UserScriptList::const_iterator iter = user_scripts->begin(); 340 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( 341 LoadUserScripts(
342 user_scripts, info, new_extensions, NULL /* no verifier for testing */); 342 user_scripts, info, added_scripts, NULL /* no verifier for testing */);
343 } 343 }
344 344
345 UserScriptMaster::UserScriptMaster(Profile* profile) 345 UserScriptLoader::UserScriptLoader(Profile* profile,
346 : user_scripts_(new UserScriptList()), 346 const ExtensionId& owner_extension_id,
347 extensions_service_ready_(false), 347 bool listen_for_extension_system_loaded)
348 : user_scripts_(new std::set<UserScript>()),
348 pending_load_(false), 349 pending_load_(false),
349 profile_(profile), 350 profile_(profile),
350 extension_registry_observer_(this), 351 owner_extension_id_(owner_extension_id),
351 weak_factory_(this) { 352 weak_factory_(this) {
352 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); 353 if (listen_for_extension_system_loaded) {
354 extension_system_ready_ = false;
355 ExtensionSystem::Get(profile_)->ready().Post(
356 FROM_HERE,
357 base::Bind(&UserScriptLoader::OnExtensionSystemReady,
358 weak_factory_.GetWeakPtr()));
359 } else {
360 extension_system_ready_ = true;
361 }
353 registrar_.Add(this, 362 registrar_.Add(this,
354 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 363 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
355 content::Source<Profile>(profile_));
356 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
357 content::NotificationService::AllBrowserContextsAndSources()); 364 content::NotificationService::AllBrowserContextsAndSources());
358 } 365 }
359 366
360 UserScriptMaster::~UserScriptMaster() { 367 UserScriptLoader::~UserScriptLoader() {
361 } 368 }
362 369
363 void UserScriptMaster::OnScriptsLoaded( 370 void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) {
364 scoped_ptr<UserScriptList> user_scripts, 371 for (std::set<UserScript>::const_iterator it = scripts.begin();
365 scoped_ptr<base::SharedMemory> shared_memory) { 372 it != scripts.end();
366 user_scripts_.reset(user_scripts.release()); 373 ++it) {
367 if (pending_load_) { 374 removed_scripts_.erase(*it);
368 // While we were loading, there were further changes. Don't bother 375 added_scripts_.insert(*it);
369 // notifying about these scripts and instead just immediately reload.
370 pending_load_ = false;
371 StartLoad();
372 return;
373 } 376 }
374 377 AttemptLoad();
375 if (shared_memory.get() == NULL) {
376 // 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,
378 // 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
380 // 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.
382
383 // Pretend the extension change didn't happen.
384 return;
385 }
386
387 // We've got scripts ready to go.
388 shared_memory_.reset(shared_memory.release());
389
390 for (content::RenderProcessHost::iterator i(
391 content::RenderProcessHost::AllHostsIterator());
392 !i.IsAtEnd(); i.Advance()) {
393 SendUpdate(i.GetCurrentValue(),
394 shared_memory_.get(),
395 changed_extensions_);
396 }
397 changed_extensions_.clear();
398
399 content::NotificationService::current()->Notify(
400 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
401 content::Source<Profile>(profile_),
402 content::Details<base::SharedMemory>(shared_memory_.get()));
403 } 378 }
404 379
405 void UserScriptMaster::OnExtensionLoaded( 380 void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) {
406 content::BrowserContext* browser_context, 381 for (std::set<UserScript>::const_iterator it = scripts.begin();
407 const Extension* extension) { 382 it != scripts.end();
408 added_extensions_.insert(extension->id()); 383 ++it) {
409 removed_extensions_.erase(extension->id()); 384 added_scripts_.erase(*it);
410 extensions_info_[extension->id()] = 385 removed_scripts_.insert(*it);
411 ExtensionSet::ExtensionPathAndDefaultLocale( 386 }
412 extension->path(), LocaleInfo::GetDefaultLocale(extension)); 387 AttemptLoad();
413 if (extensions_service_ready_) { 388 }
414 changed_extensions_.insert(extension->id()); 389
390 void UserScriptLoader::ClearScripts() {
391 clear_scripts_ = true;
392 added_scripts_.clear();
393 removed_scripts_.clear();
394 AttemptLoad();
395 }
396
397 void UserScriptLoader::OnExtensionSystemReady() {
398 extension_system_ready_ = true;
399 AttemptLoad();
400 }
401
402 void UserScriptLoader::Observe(int type,
403 const content::NotificationSource& source,
404 const content::NotificationDetails& details) {
405 DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED);
406 content::RenderProcessHost* process =
407 content::Source<content::RenderProcessHost>(source).ptr();
408 Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
409 if (!profile_->IsSameProfile(profile))
410 return;
411 if (scripts_ready()) {
412 SendUpdate(process,
413 GetSharedMemory(),
414 std::set<ExtensionId>()); // Include all extensions.
415 }
416 AttemptLoad();
417 }
418
419 void UserScriptLoader::AttemptLoad() {
420 if (extension_system_ready_) {
415 if (is_loading()) 421 if (is_loading())
416 pending_load_ = true; 422 pending_load_ = true;
417 else 423 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(); 424 StartLoad();
469 } 425 }
470 } 426 }
471 427
472 void UserScriptMaster::StartLoad() { 428 void UserScriptLoader::StartLoad() {
473 DCHECK_CURRENTLY_ON(BrowserThread::UI); 429 DCHECK_CURRENTLY_ON(BrowserThread::UI);
474 DCHECK(!is_loading()); 430 DCHECK(!is_loading());
475 431
476 // Remove any user scripts belonging to any extension that was updated or 432 // If scripts were marked for clearing before adding and removing, then clear
477 // removed. 433 // them.
478 for (UserScriptList::iterator iter = user_scripts_->begin(); 434 if (clear_scripts_) {
479 iter != user_scripts_->end();) { 435 user_scripts_->clear();
480 if (removed_extensions_.count(iter->extension_id()) > 0 || 436 } else {
481 added_extensions_.count(iter->extension_id()) > 0) { 437 // Remove scripts marked for removal.
Devlin 2014/08/05 21:47:27 Let's lose this and the "Add scripts marked for ad
Mark Dittmer 2014/08/06 15:32:25 Done.
482 iter = user_scripts_->erase(iter); 438 for (std::set<UserScript>::iterator it = removed_scripts_.begin();
483 } else { 439 it != removed_scripts_.end();
484 ++iter; 440 ++it) {
441 user_scripts_->erase(it);
485 } 442 }
486 } 443 }
487 444
488 // Add any content scripts for extensions that were recently loaded. 445 // Add scripts marked for addition.
489 const ExtensionSet& enabled_extensions = 446 user_scripts_->insert(added_scripts_.begin(), added_scripts_.end());
490 ExtensionRegistry::Get(profile_)->enabled_extensions(); 447
491 for (std::set<std::string>::const_iterator iter = added_extensions_.begin(); 448 // Recompute |changed_extensions_| for OnScriptsLoaded, which will use it in
492 iter != added_extensions_.end(); ++iter) { 449 // its IPC message. this must be done before we clear |added_scripts_| and
493 const Extension* extension = enabled_extensions.GetByID(*iter); 450 // |removed_scripts_| below.
494 if (!extension) 451 ComputeChangedExtensions();
495 continue; 452
496 bool incognito_enabled = 453 // Update |extensions_info_| to contain info from every extension in
497 util::IsIncognitoEnabled(extension->id(), profile_); 454 // |changed_extensions_| before passing it to LoadScriptsOnFileThread.
498 const UserScriptList& scripts = 455 UpdateExtensionsInfo();
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 456
508 BrowserThread::PostTask( 457 BrowserThread::PostTask(
509 BrowserThread::FILE, 458 BrowserThread::FILE,
510 FROM_HERE, 459 FROM_HERE,
511 base::Bind(&LoadScriptsOnFileThread, 460 base::Bind(&LoadScriptsOnFileThread,
512 base::Passed(&user_scripts_), 461 base::Passed(&user_scripts_),
513 extensions_info_, 462 extensions_info_,
514 added_extensions_, 463 added_scripts_,
515 make_scoped_refptr( 464 make_scoped_refptr(
516 ExtensionSystem::Get(profile_)->content_verifier()), 465 ExtensionSystem::Get(profile_)->content_verifier()),
517 base::Bind(&UserScriptMaster::OnScriptsLoaded, 466 base::Bind(&UserScriptLoader::OnScriptsLoaded,
518 weak_factory_.GetWeakPtr()))); 467 weak_factory_.GetWeakPtr())));
519 added_extensions_.clear(); 468
520 removed_extensions_.clear(); 469 clear_scripts_ = false;
470 added_scripts_.clear();
471 removed_scripts_.clear();
521 user_scripts_.reset(NULL); 472 user_scripts_.reset(NULL);
522 } 473 }
523 474
524 void UserScriptMaster::SendUpdate( 475 void UserScriptLoader::OnScriptsLoaded(
476 scoped_ptr<std::set<UserScript> > user_scripts,
477 scoped_ptr<base::SharedMemory> shared_memory) {
478 user_scripts_.reset(user_scripts.release());
479 if (pending_load_) {
480 // While we were loading, there were further changes. Don't bother
481 // notifying about these scripts and instead just immediately reload.
482 pending_load_ = false;
483 StartLoad();
484 return;
485 }
486
487 if (shared_memory.get() == NULL) {
488 // This can happen if we run out of file descriptors. In that case, we
489 // have a choice between silently omitting all user scripts for new tabs,
490 // by nulling out shared_memory_, or only silently omitting new ones by
491 // leaving the existing object in place. The second seems less bad, even
492 // though it removes the possibility that freeing the shared memory block
493 // would open up enough FDs for long enough for a retry to succeed.
494
495 // Pretend the extension change didn't happen.
496 return;
497 }
498
499 // We've got scripts ready to go.
500 shared_memory_.reset(shared_memory.release());
501
502 for (content::RenderProcessHost::iterator i(
503 content::RenderProcessHost::AllHostsIterator());
504 !i.IsAtEnd();
505 i.Advance()) {
506 SendUpdate(i.GetCurrentValue(), shared_memory_.get(), changed_extensions_);
507 }
508
509 content::NotificationService::current()->Notify(
510 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
511 content::Source<Profile>(profile_),
512 content::Details<base::SharedMemory>(shared_memory_.get()));
513 }
514
515 void UserScriptLoader::SendUpdate(
525 content::RenderProcessHost* process, 516 content::RenderProcessHost* process,
526 base::SharedMemory* shared_memory, 517 base::SharedMemory* shared_memory,
527 const std::set<std::string>& changed_extensions) { 518 const std::set<ExtensionId>& changed_extensions) {
528 // Don't allow injection of content scripts into <webview>. 519 // Don't allow injection of content scripts into <webview>.
529 if (process->IsIsolatedGuest()) 520 if (process->IsIsolatedGuest())
530 return; 521 return;
531 522
532 Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext()); 523 Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
533 // Make sure we only send user scripts to processes in our profile. 524 // Make sure we only send user scripts to processes in our profile.
534 if (!profile_->IsSameProfile(profile)) 525 if (!profile_->IsSameProfile(profile))
535 return; 526 return;
536 527
537 // If the process is being started asynchronously, early return. We'll end up 528 // 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. 529 // calling InitUserScripts when it's created which will call this again.
539 base::ProcessHandle handle = process->GetHandle(); 530 base::ProcessHandle handle = process->GetHandle();
540 if (!handle) 531 if (!handle)
541 return; 532 return;
542 533
543 base::SharedMemoryHandle handle_for_process; 534 base::SharedMemoryHandle handle_for_process;
544 if (!shared_memory->ShareToProcess(handle, &handle_for_process)) 535 if (!shared_memory->ShareToProcess(handle, &handle_for_process))
545 return; // This can legitimately fail if the renderer asserts at startup. 536 return; // This can legitimately fail if the renderer asserts at startup.
546 537
547 if (base::SharedMemory::IsHandleValid(handle_for_process)) { 538 if (base::SharedMemory::IsHandleValid(handle_for_process)) {
548 process->Send(new ExtensionMsg_UpdateUserScripts( 539 process->Send(new ExtensionMsg_UpdateUserScripts(
549 handle_for_process, "" /* owner */, changed_extensions)); 540 handle_for_process, "" /* owner */, changed_extensions));
550 } 541 }
551 } 542 }
552 543
544 void UserScriptLoader::ComputeChangedExtensions() {
Devlin 2014/08/05 21:47:27 It looks like this does away with the empty-set-me
Mark Dittmer 2014/08/06 15:32:25 I don't think so. This does the same thing we did
545 changed_extensions_.clear();
546 for (std::set<UserScript>::const_iterator it = removed_scripts_.begin();
547 it != removed_scripts_.end();
548 ++it) {
549 changed_extensions_.insert(it->extension_id());
550 }
551 for (std::set<UserScript>::const_iterator it = added_scripts_.begin();
552 it != added_scripts_.end();
553 ++it) {
554 changed_extensions_.insert(it->extension_id());
555 }
556 }
557
558 void UserScriptLoader::UpdateExtensionsInfo() {
Devlin 2014/08/05 21:47:27 Do we need to worry about removing outdated extens
Mark Dittmer 2014/08/06 15:32:25 Excellent point. I've implemented this with a publ
Devlin 2014/08/06 15:59:25 It should probably be the job of UserScriptLoader.
Mark Dittmer 2014/08/06 22:08:18 Right. Done.
559 for (std::set<ExtensionId>::const_iterator it = changed_extensions_.begin();
560 it != changed_extensions_.end();
561 ++it) {
562 if (extensions_info_.find(*it) == extensions_info_.end()) {
563 const Extension* extension =
564 ExtensionRegistry::Get(profile_)
565 ->GetExtensionById(*it, ExtensionRegistry::EVERYTHING);
566 extensions_info_[*it] = ExtensionSet::ExtensionPathAndDefaultLocale(
567 extension->path(), LocaleInfo::GetDefaultLocale(extension));
568 }
569 }
570 }
571
553 } // namespace extensions 572 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698