OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |