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

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

Powered by Google App Engine
This is Rietveld 408576698