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

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

Powered by Google App Engine
This is Rietveld 408576698