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