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

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

Powered by Google App Engine
This is Rietveld 408576698