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

Side by Side Diff: chrome/browser/extensions/user_script_master.cc

Issue 362343006: Fix dangerous pointer use in UserScriptMaster (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 5 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 (c) 2012 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_master.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/bind_helpers.h" 10 #include "base/bind_helpers.h"
11 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/files/file_path.h" 12 #include "base/files/file_path.h"
13 #include "base/memory/shared_memory.h"
13 #include "base/version.h" 14 #include "base/version.h"
14 #include "chrome/browser/chrome_notification_types.h" 15 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_util.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 "chrome/common/extensions/manifest_handlers/content_scripts_handler.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/file_util.h" 27 #include "extensions/common/file_util.h"
28 #include "extensions/common/message_bundle.h" 28 #include "extensions/common/message_bundle.h"
29 #include "ui/base/resource/resource_bundle.h" 29 #include "ui/base/resource/resource_bundle.h"
30 30
31 using content::BrowserThread; 31 using content::BrowserThread;
32 using extensions::ExtensionsBrowserClient; 32 using extensions::ExtensionsBrowserClient;
33 33
34 namespace extensions { 34 namespace extensions {
35 35
36 // Helper function to parse greasesmonkey headers 36 namespace {
37 static bool GetDeclarationValue(const base::StringPiece& line,
38 const base::StringPiece& prefix,
39 std::string* value) {
40 base::StringPiece::size_type index = line.find(prefix);
41 if (index == base::StringPiece::npos)
42 return false;
43 37
44 std::string temp(line.data() + index + prefix.length(), 38 typedef base::Callback<void(scoped_ptr<UserScriptList>,
45 line.length() - index - prefix.length()); 39 scoped_ptr<base::SharedMemory>)>
40 LoadScriptsCallback;
46 41
47 if (temp.empty() || !IsWhitespace(temp[0])) 42 void VerifyContent(scoped_refptr<ContentVerifier> verifier,
48 return false; 43 const std::string& extension_id,
49 44 const base::FilePath& extension_root,
50 base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value); 45 const base::FilePath& relative_path,
51 return true; 46 const std::string& content) {
52 }
53
54 UserScriptMaster::ScriptReloader::ScriptReloader(UserScriptMaster* master)
55 : master_(master) {
56 CHECK(BrowserThread::GetCurrentThreadIdentifier(&master_thread_id_));
57 }
58
59 // static
60 bool UserScriptMaster::ScriptReloader::ParseMetadataHeader(
61 const base::StringPiece& script_text, UserScript* script) {
62 // http://wiki.greasespot.net/Metadata_block
63 base::StringPiece line;
64 size_t line_start = 0;
65 size_t line_end = line_start;
66 bool in_metadata = false;
67
68 static const base::StringPiece kUserScriptBegin("// ==UserScript==");
69 static const base::StringPiece kUserScriptEng("// ==/UserScript==");
70 static const base::StringPiece kNamespaceDeclaration("// @namespace");
71 static const base::StringPiece kNameDeclaration("// @name");
72 static const base::StringPiece kVersionDeclaration("// @version");
73 static const base::StringPiece kDescriptionDeclaration("// @description");
74 static const base::StringPiece kIncludeDeclaration("// @include");
75 static const base::StringPiece kExcludeDeclaration("// @exclude");
76 static const base::StringPiece kMatchDeclaration("// @match");
77 static const base::StringPiece kExcludeMatchDeclaration("// @exclude_match");
78 static const base::StringPiece kRunAtDeclaration("// @run-at");
79 static const base::StringPiece kRunAtDocumentStartValue("document-start");
80 static const base::StringPiece kRunAtDocumentEndValue("document-end");
81 static const base::StringPiece kRunAtDocumentIdleValue("document-idle");
82
83 while (line_start < script_text.length()) {
84 line_end = script_text.find('\n', line_start);
85
86 // Handle the case where there is no trailing newline in the file.
87 if (line_end == std::string::npos)
88 line_end = script_text.length() - 1;
89
90 line.set(script_text.data() + line_start, line_end - line_start);
91
92 if (!in_metadata) {
93 if (line.starts_with(kUserScriptBegin))
94 in_metadata = true;
95 } else {
96 if (line.starts_with(kUserScriptEng))
97 break;
98
99 std::string value;
100 if (GetDeclarationValue(line, kIncludeDeclaration, &value)) {
101 // We escape some characters that MatchPattern() considers special.
102 ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
103 ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
104 script->add_glob(value);
105 } else if (GetDeclarationValue(line, kExcludeDeclaration, &value)) {
106 ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
107 ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
108 script->add_exclude_glob(value);
109 } else if (GetDeclarationValue(line, kNamespaceDeclaration, &value)) {
110 script->set_name_space(value);
111 } else if (GetDeclarationValue(line, kNameDeclaration, &value)) {
112 script->set_name(value);
113 } else if (GetDeclarationValue(line, kVersionDeclaration, &value)) {
114 Version version(value);
115 if (version.IsValid())
116 script->set_version(version.GetString());
117 } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) {
118 script->set_description(value);
119 } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) {
120 URLPattern pattern(UserScript::ValidUserScriptSchemes());
121 if (URLPattern::PARSE_SUCCESS != pattern.Parse(value))
122 return false;
123 script->add_url_pattern(pattern);
124 } else if (GetDeclarationValue(line, kExcludeMatchDeclaration, &value)) {
125 URLPattern exclude(UserScript::ValidUserScriptSchemes());
126 if (URLPattern::PARSE_SUCCESS != exclude.Parse(value))
127 return false;
128 script->add_exclude_url_pattern(exclude);
129 } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) {
130 if (value == kRunAtDocumentStartValue)
131 script->set_run_location(UserScript::DOCUMENT_START);
132 else if (value == kRunAtDocumentEndValue)
133 script->set_run_location(UserScript::DOCUMENT_END);
134 else if (value == kRunAtDocumentIdleValue)
135 script->set_run_location(UserScript::DOCUMENT_IDLE);
136 else
137 return false;
138 }
139
140 // TODO(aa): Handle more types of metadata.
141 }
142
143 line_start = line_end + 1;
144 }
145
146 // If no patterns were specified, default to @include *. This is what
147 // Greasemonkey does.
148 if (script->globs().empty() && script->url_patterns().is_empty())
149 script->add_glob("*");
150
151 return true;
152 }
153
154 void UserScriptMaster::ScriptReloader::StartLoad(
155 const UserScriptList& user_scripts,
156 const ExtensionsInfo& extensions_info) {
157 // Add a reference to ourselves to keep ourselves alive while we're running.
158 // Balanced by NotifyMaster().
159 AddRef();
160
161 verifier_ = master_->content_verifier();
162 this->extensions_info_ = extensions_info;
163 BrowserThread::PostTask(
164 BrowserThread::FILE, FROM_HERE,
165 base::Bind(
166 &UserScriptMaster::ScriptReloader::RunLoad, this, user_scripts));
167 }
168
169 UserScriptMaster::ScriptReloader::~ScriptReloader() {}
170
171 void UserScriptMaster::ScriptReloader::NotifyMaster(
172 scoped_ptr<base::SharedMemory> memory) {
173 // The master could go away
174 if (master_)
175 master_->NewScriptsAvailable(memory.Pass());
176
177 // Drop our self-reference.
178 // Balances StartLoad().
179 Release();
180 }
181
182 static void VerifyContent(ContentVerifier* verifier,
183 const std::string& extension_id,
184 const base::FilePath& extension_root,
185 const base::FilePath& relative_path,
186 const std::string& content) {
187 scoped_refptr<ContentVerifyJob> job( 47 scoped_refptr<ContentVerifyJob> job(
188 verifier->CreateJobFor(extension_id, extension_root, relative_path)); 48 verifier->CreateJobFor(extension_id, extension_root, relative_path));
189 if (job.get()) { 49 if (job.get()) {
190 job->Start(); 50 job->Start();
191 job->BytesRead(content.size(), content.data()); 51 job->BytesRead(content.size(), content.data());
192 job->DoneReading(); 52 job->DoneReading();
193 } 53 }
194 } 54 }
195 55
196 static bool LoadScriptContent(const std::string& extension_id, 56 bool LoadScriptContent(const std::string& extension_id,
197 UserScript::File* script_file, 57 UserScript::File* script_file,
198 const SubstitutionMap* localization_messages, 58 const SubstitutionMap* localization_messages,
199 ContentVerifier* verifier) { 59 scoped_refptr<ContentVerifier> verifier) {
200 std::string content; 60 std::string content;
201 const base::FilePath& path = ExtensionResource::GetFilePath( 61 const base::FilePath& path = ExtensionResource::GetFilePath(
202 script_file->extension_root(), script_file->relative_path(), 62 script_file->extension_root(), script_file->relative_path(),
203 ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT); 63 ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT);
204 if (path.empty()) { 64 if (path.empty()) {
205 int resource_id; 65 int resource_id;
206 if (ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()-> 66 if (ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()->
207 IsComponentExtensionResource( 67 IsComponentExtensionResource(
208 script_file->extension_root(), script_file->relative_path(), 68 script_file->extension_root(), script_file->relative_path(),
209 &resource_id)) { 69 &resource_id)) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 std::string::size_type index = content.find(base::kUtf8ByteOrderMark); 103 std::string::size_type index = content.find(base::kUtf8ByteOrderMark);
244 if (index == 0) { 104 if (index == 0) {
245 script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark))); 105 script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark)));
246 } else { 106 } else {
247 script_file->set_content(content); 107 script_file->set_content(content);
248 } 108 }
249 109
250 return true; 110 return true;
251 } 111 }
252 112
253 void UserScriptMaster::ScriptReloader::LoadUserScripts( 113 SubstitutionMap* GetLocalizationMessages(const ExtensionsInfo& extensions_info,
254 UserScriptList* user_scripts) { 114 const std::string& extension_id) {
115 ExtensionsInfo::const_iterator iter = extensions_info.find(extension_id);
116 if (iter == extensions_info.end())
117 return NULL;
118 return file_util::LoadMessageBundleSubstitutionMap(iter->second.first,
119 extension_id,
120 iter->second.second);
121 }
122
123 void LoadUserScripts(UserScriptList* user_scripts,
124 const ExtensionsInfo& extensions_info,
125 const std::set<std::string>& new_extensions,
126 ContentVerifier* verifier) {
255 for (size_t i = 0; i < user_scripts->size(); ++i) { 127 for (size_t i = 0; i < user_scripts->size(); ++i) {
256 UserScript& script = user_scripts->at(i); 128 UserScript& script = user_scripts->at(i);
129 if (new_extensions.count(script.extension_id()) == 0)
130 continue;
257 scoped_ptr<SubstitutionMap> localization_messages( 131 scoped_ptr<SubstitutionMap> localization_messages(
258 GetLocalizationMessages(script.extension_id())); 132 GetLocalizationMessages(extensions_info, script.extension_id()));
259 for (size_t k = 0; k < script.js_scripts().size(); ++k) { 133 for (size_t k = 0; k < script.js_scripts().size(); ++k) {
260 UserScript::File& script_file = script.js_scripts()[k]; 134 UserScript::File& script_file = script.js_scripts()[k];
261 if (script_file.GetContent().empty()) 135 if (script_file.GetContent().empty())
262 LoadScriptContent( 136 LoadScriptContent(
263 script.extension_id(), &script_file, NULL, verifier_.get()); 137 script.extension_id(), &script_file, NULL, verifier);
264 } 138 }
265 for (size_t k = 0; k < script.css_scripts().size(); ++k) { 139 for (size_t k = 0; k < script.css_scripts().size(); ++k) {
266 UserScript::File& script_file = script.css_scripts()[k]; 140 UserScript::File& script_file = script.css_scripts()[k];
267 if (script_file.GetContent().empty()) 141 if (script_file.GetContent().empty())
268 LoadScriptContent(script.extension_id(), 142 LoadScriptContent(script.extension_id(),
269 &script_file, 143 &script_file,
270 localization_messages.get(), 144 localization_messages.get(),
271 verifier_.get()); 145 verifier);
272 } 146 }
273 } 147 }
274 } 148 }
275 149
276 SubstitutionMap* UserScriptMaster::ScriptReloader::GetLocalizationMessages(
277 const std::string& extension_id) {
278 if (extensions_info_.find(extension_id) == extensions_info_.end()) {
279 return NULL;
280 }
281
282 return file_util::LoadMessageBundleSubstitutionMap(
283 extensions_info_[extension_id].first,
284 extension_id,
285 extensions_info_[extension_id].second);
286 }
287
288 // Pickle user scripts and return pointer to the shared memory. 150 // Pickle user scripts and return pointer to the shared memory.
289 static scoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) { 151 scoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) {
290 Pickle pickle; 152 Pickle pickle;
291 pickle.WriteUInt64(scripts.size()); 153 pickle.WriteUInt64(scripts.size());
292 for (size_t i = 0; i < scripts.size(); i++) { 154 for (size_t i = 0; i < scripts.size(); i++) {
293 const UserScript& script = scripts[i]; 155 const UserScript& script = scripts[i];
294 // TODO(aa): This can be replaced by sending content script metadata to 156 // TODO(aa): This can be replaced by sending content script metadata to
295 // renderers along with other extension data in ExtensionMsg_Loaded. 157 // renderers along with other extension data in ExtensionMsg_Loaded.
296 // See crbug.com/70516. 158 // See crbug.com/70516.
297 script.Pickle(&pickle); 159 script.Pickle(&pickle);
298 // Write scripts as 'data' so that we can read it out in the slave without 160 // Write scripts as 'data' so that we can read it out in the slave without
299 // allocating a new string. 161 // allocating a new string.
(...skipping 24 matching lines...) Expand all
324 186
325 base::SharedMemoryHandle readonly_handle; 187 base::SharedMemoryHandle readonly_handle;
326 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(), 188 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(),
327 &readonly_handle)) 189 &readonly_handle))
328 return scoped_ptr<base::SharedMemory>(); 190 return scoped_ptr<base::SharedMemory>();
329 191
330 return make_scoped_ptr(new base::SharedMemory(readonly_handle, 192 return make_scoped_ptr(new base::SharedMemory(readonly_handle,
331 /*read_only=*/true)); 193 /*read_only=*/true));
332 } 194 }
333 195
334 // This method will be called on the file thread. 196 void LoadScriptsOnFileThread(scoped_ptr<UserScriptList> user_scripts,
335 void UserScriptMaster::ScriptReloader::RunLoad( 197 const ExtensionsInfo& extensions_info,
336 const UserScriptList& user_scripts) { 198 const std::set<std::string>& new_extensions,
337 LoadUserScripts(const_cast<UserScriptList*>(&user_scripts)); 199 scoped_refptr<ContentVerifier> verifier,
200 LoadScriptsCallback callback) {
201 DCHECK(user_scripts.get());
202 LoadUserScripts(
203 user_scripts.get(), extensions_info, new_extensions, verifier);
204 scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts);
205 BrowserThread::PostTask(
206 BrowserThread::UI,
207 FROM_HERE,
208 base::Bind(callback,
209 base::Passed(&user_scripts),
210 base::Passed(&memory)));
211 }
338 212
339 // Scripts now contains list of up-to-date scripts. Load the content in the 213 // Helper function to parse greasesmonkey headers
340 // shared memory and let the master know it's ready. We need to post the task 214 bool GetDeclarationValue(const base::StringPiece& line,
341 // back even if no scripts ware found to balance the AddRef/Release calls. 215 const base::StringPiece& prefix,
342 BrowserThread::PostTask(master_thread_id_, 216 std::string* value) {
343 FROM_HERE, 217 base::StringPiece::size_type index = line.find(prefix);
344 base::Bind(&ScriptReloader::NotifyMaster, 218 if (index == base::StringPiece::npos)
345 this, 219 return false;
346 base::Passed(Serialize(user_scripts)))); 220
221 std::string temp(line.data() + index + prefix.length(),
222 line.length() - index - prefix.length());
223
224 if (temp.empty() || !IsWhitespace(temp[0]))
225 return false;
226
227 base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value);
228 return true;
229 }
230
231 } // namespace
232
233 // static
234 bool UserScriptMaster::ParseMetadataHeader(
235 const base::StringPiece& script_text, UserScript* script) {
236 // http://wiki.greasespot.net/Metadata_block
237 base::StringPiece line;
238 size_t line_start = 0;
239 size_t line_end = line_start;
240 bool in_metadata = false;
241
242 static const base::StringPiece kUserScriptBegin("// ==UserScript==");
243 static const base::StringPiece kUserScriptEng("// ==/UserScript==");
244 static const base::StringPiece kNamespaceDeclaration("// @namespace");
245 static const base::StringPiece kNameDeclaration("// @name");
246 static const base::StringPiece kVersionDeclaration("// @version");
247 static const base::StringPiece kDescriptionDeclaration("// @description");
248 static const base::StringPiece kIncludeDeclaration("// @include");
249 static const base::StringPiece kExcludeDeclaration("// @exclude");
250 static const base::StringPiece kMatchDeclaration("// @match");
251 static const base::StringPiece kExcludeMatchDeclaration("// @exclude_match");
252 static const base::StringPiece kRunAtDeclaration("// @run-at");
253 static const base::StringPiece kRunAtDocumentStartValue("document-start");
254 static const base::StringPiece kRunAtDocumentEndValue("document-end");
255 static const base::StringPiece kRunAtDocumentIdleValue("document-idle");
256
257 while (line_start < script_text.length()) {
258 line_end = script_text.find('\n', line_start);
259
260 // Handle the case where there is no trailing newline in the file.
261 if (line_end == std::string::npos)
262 line_end = script_text.length() - 1;
263
264 line.set(script_text.data() + line_start, line_end - line_start);
265
266 if (!in_metadata) {
267 if (line.starts_with(kUserScriptBegin))
268 in_metadata = true;
269 } else {
270 if (line.starts_with(kUserScriptEng))
271 break;
272
273 std::string value;
274 if (GetDeclarationValue(line, kIncludeDeclaration, &value)) {
275 // We escape some characters that MatchPattern() considers special.
276 ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
277 ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
278 script->add_glob(value);
279 } else if (GetDeclarationValue(line, kExcludeDeclaration, &value)) {
280 ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
281 ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
282 script->add_exclude_glob(value);
283 } else if (GetDeclarationValue(line, kNamespaceDeclaration, &value)) {
284 script->set_name_space(value);
285 } else if (GetDeclarationValue(line, kNameDeclaration, &value)) {
286 script->set_name(value);
287 } else if (GetDeclarationValue(line, kVersionDeclaration, &value)) {
288 Version version(value);
289 if (version.IsValid())
290 script->set_version(version.GetString());
291 } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) {
292 script->set_description(value);
293 } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) {
294 URLPattern pattern(UserScript::ValidUserScriptSchemes());
295 if (URLPattern::PARSE_SUCCESS != pattern.Parse(value))
296 return false;
297 script->add_url_pattern(pattern);
298 } else if (GetDeclarationValue(line, kExcludeMatchDeclaration, &value)) {
299 URLPattern exclude(UserScript::ValidUserScriptSchemes());
300 if (URLPattern::PARSE_SUCCESS != exclude.Parse(value))
301 return false;
302 script->add_exclude_url_pattern(exclude);
303 } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) {
304 if (value == kRunAtDocumentStartValue)
305 script->set_run_location(UserScript::DOCUMENT_START);
306 else if (value == kRunAtDocumentEndValue)
307 script->set_run_location(UserScript::DOCUMENT_END);
308 else if (value == kRunAtDocumentIdleValue)
309 script->set_run_location(UserScript::DOCUMENT_IDLE);
310 else
311 return false;
312 }
313
314 // TODO(aa): Handle more types of metadata.
315 }
316
317 line_start = line_end + 1;
318 }
319
320 // If no patterns were specified, default to @include *. This is what
321 // Greasemonkey does.
322 if (script->globs().empty() && script->url_patterns().is_empty())
323 script->add_glob("*");
324
325 return true;
326 }
327
328 // static
329 void UserScriptMaster::LoadScriptsForTest(UserScriptList* user_scripts) {
330 ExtensionsInfo info;
331 std::set<std::string> new_extensions;
332 for (UserScriptList::const_iterator iter = user_scripts->begin();
333 iter != user_scripts->end();
334 ++iter) {
335 new_extensions.insert(iter->extension_id());
336 }
337 LoadUserScripts(
338 user_scripts, info, new_extensions, NULL /* no verifier for testing */);
347 } 339 }
348 340
349 UserScriptMaster::UserScriptMaster(Profile* profile) 341 UserScriptMaster::UserScriptMaster(Profile* profile)
350 : extensions_service_ready_(false), 342 : user_scripts_(new UserScriptList()),
343 extensions_service_ready_(false),
351 pending_load_(false), 344 pending_load_(false),
352 profile_(profile), 345 profile_(profile),
353 extension_registry_observer_(this) { 346 extension_registry_observer_(this),
347 weak_factory_(this) {
354 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); 348 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
355 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY, 349 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
356 content::Source<Profile>(profile_)); 350 content::Source<Profile>(profile_));
357 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED, 351 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
358 content::NotificationService::AllBrowserContextsAndSources()); 352 content::NotificationService::AllBrowserContextsAndSources());
359 } 353 }
360 354
361 UserScriptMaster::~UserScriptMaster() { 355 UserScriptMaster::~UserScriptMaster() {
362 if (script_reloader_.get())
363 script_reloader_->DisownMaster();
364 } 356 }
365 357
366 void UserScriptMaster::NewScriptsAvailable( 358 void UserScriptMaster::OnScriptsLoaded(
367 scoped_ptr<base::SharedMemory> handle) { 359 scoped_ptr<UserScriptList> user_scripts,
360 scoped_ptr<base::SharedMemory> shared_memory) {
361 user_scripts_.reset(user_scripts.release());
368 if (pending_load_) { 362 if (pending_load_) {
369 // While we were loading, there were further changes. Don't bother 363 // While we were loading, there were further changes. Don't bother
370 // notifying about these scripts and instead just immediately reload. 364 // notifying about these scripts and instead just immediately reload.
371 pending_load_ = false; 365 pending_load_ = false;
372 StartLoad(); 366 StartLoad();
373 } else { 367 return;
374 // We're no longer loading. 368 }
375 script_reloader_ = NULL;
376 369
377 if (handle == NULL) { 370 if (shared_memory.get() == NULL) {
378 // This can happen if we run out of file descriptors. In that case, we 371 // 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, 372 // 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 373 // 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 374 // leaving the existing object in place. The second seems less bad, even
382 // though it removes the possibility that freeing the shared memory block 375 // 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. 376 // would open up enough FDs for long enough for a retry to succeed.
384 377
385 // Pretend the extension change didn't happen. 378 // Pretend the extension change didn't happen.
386 return; 379 return;
387 } 380 }
388 381
389 // We've got scripts ready to go. 382 // We've got scripts ready to go.
390 shared_memory_ = handle.Pass(); 383 shared_memory_.reset(shared_memory.release());
391 384
392 for (content::RenderProcessHost::iterator i( 385 for (content::RenderProcessHost::iterator i(
393 content::RenderProcessHost::AllHostsIterator()); 386 content::RenderProcessHost::AllHostsIterator());
394 !i.IsAtEnd(); i.Advance()) { 387 !i.IsAtEnd(); i.Advance()) {
395 SendUpdate(i.GetCurrentValue(), 388 SendUpdate(i.GetCurrentValue(),
396 shared_memory_.get(), 389 shared_memory_.get(),
397 changed_extensions_); 390 changed_extensions_);
398 } 391 }
399 changed_extensions_.clear(); 392 changed_extensions_.clear();
400 393
401 content::NotificationService::current()->Notify( 394 content::NotificationService::current()->Notify(
402 chrome::NOTIFICATION_USER_SCRIPTS_UPDATED, 395 chrome::NOTIFICATION_USER_SCRIPTS_UPDATED,
403 content::Source<Profile>(profile_), 396 content::Source<Profile>(profile_),
404 content::Details<base::SharedMemory>(shared_memory_.get())); 397 content::Details<base::SharedMemory>(shared_memory_.get()));
405 }
406 }
407
408 ContentVerifier* UserScriptMaster::content_verifier() {
409 ExtensionSystem* system = ExtensionSystem::Get(profile_);
410 return system->content_verifier();
411 } 398 }
412 399
413 void UserScriptMaster::OnExtensionLoaded( 400 void UserScriptMaster::OnExtensionLoaded(
414 content::BrowserContext* browser_context, 401 content::BrowserContext* browser_context,
415 const Extension* extension) { 402 const Extension* extension) {
416 // Add any content scripts inside the extension. 403 added_extensions_.insert(extension->id());
404 removed_extensions_.erase(extension->id());
417 extensions_info_[extension->id()] = 405 extensions_info_[extension->id()] =
418 ExtensionSet::ExtensionPathAndDefaultLocale( 406 ExtensionSet::ExtensionPathAndDefaultLocale(
419 extension->path(), LocaleInfo::GetDefaultLocale(extension)); 407 extension->path(), LocaleInfo::GetDefaultLocale(extension));
420 bool incognito_enabled = util::IsIncognitoEnabled(extension->id(), profile_);
421 const UserScriptList& scripts =
422 ContentScriptsInfo::GetContentScripts(extension);
423 for (UserScriptList::const_iterator iter = scripts.begin();
424 iter != scripts.end();
425 ++iter) {
426 user_scripts_.push_back(*iter);
427 user_scripts_.back().set_incognito_enabled(incognito_enabled);
428 }
429 if (extensions_service_ready_) { 408 if (extensions_service_ready_) {
430 changed_extensions_.insert(extension->id()); 409 changed_extensions_.insert(extension->id());
431 if (script_reloader_.get()) { 410 if (is_loading())
432 pending_load_ = true; 411 pending_load_ = true;
433 } else { 412 else
434 StartLoad(); 413 StartLoad();
435 }
436 } 414 }
437 } 415 }
438 416
439 void UserScriptMaster::OnExtensionUnloaded( 417 void UserScriptMaster::OnExtensionUnloaded(
440 content::BrowserContext* browser_context, 418 content::BrowserContext* browser_context,
441 const Extension* extension, 419 const Extension* extension,
442 UnloadedExtensionInfo::Reason reason) { 420 UnloadedExtensionInfo::Reason reason) {
421 removed_extensions_.insert(extension->id());
422 added_extensions_.erase(extension->id());
443 // Remove any content scripts. 423 // Remove any content scripts.
444 extensions_info_.erase(extension->id()); 424 extensions_info_.erase(extension->id());
445 UserScriptList new_user_scripts;
446 for (UserScriptList::iterator iter = user_scripts_.begin();
447 iter != user_scripts_.end();
448 ++iter) {
449 if (iter->extension_id() != extension->id())
450 new_user_scripts.push_back(*iter);
451 }
452 user_scripts_ = new_user_scripts;
453 changed_extensions_.insert(extension->id()); 425 changed_extensions_.insert(extension->id());
454 if (script_reloader_.get()) { 426 if (is_loading())
455 pending_load_ = true; 427 pending_load_ = true;
456 } else { 428 else
457 StartLoad(); 429 StartLoad();
458 }
459 } 430 }
460 431
461 void UserScriptMaster::Observe(int type, 432 void UserScriptMaster::Observe(int type,
462 const content::NotificationSource& source, 433 const content::NotificationSource& source,
463 const content::NotificationDetails& details) { 434 const content::NotificationDetails& details) {
464 bool should_start_load = false; 435 bool should_start_load = false;
465 switch (type) { 436 switch (type) {
466 case chrome::NOTIFICATION_EXTENSIONS_READY: 437 case chrome::NOTIFICATION_EXTENSIONS_READY:
467 extensions_service_ready_ = true; 438 extensions_service_ready_ = true;
468 should_start_load = true; 439 should_start_load = true;
(...skipping 10 matching lines...) Expand all
479 GetSharedMemory(), 450 GetSharedMemory(),
480 std::set<std::string>()); // Include all extensions. 451 std::set<std::string>()); // Include all extensions.
481 } 452 }
482 break; 453 break;
483 } 454 }
484 default: 455 default:
485 DCHECK(false); 456 DCHECK(false);
486 } 457 }
487 458
488 if (should_start_load) { 459 if (should_start_load) {
489 if (script_reloader_.get()) { 460 if (is_loading())
490 pending_load_ = true; 461 pending_load_ = true;
491 } else { 462 else
492 StartLoad(); 463 StartLoad();
493 }
494 } 464 }
495 } 465 }
496 466
497 void UserScriptMaster::StartLoad() { 467 void UserScriptMaster::StartLoad() {
498 if (!script_reloader_.get()) 468 DCHECK_CURRENTLY_ON(BrowserThread::UI);
499 script_reloader_ = new ScriptReloader(this); 469 DCHECK(!is_loading());
500 470
501 script_reloader_->StartLoad(user_scripts_, extensions_info_); 471 // Remove any user scripts belonging to any extension that was updated or
472 // removed.
473 for (UserScriptList::iterator iter = user_scripts_->begin();
474 iter != user_scripts_->end();) {
475 if (removed_extensions_.count(iter->extension_id()) > 0 ||
476 added_extensions_.count(iter->extension_id()) > 0) {
477 iter = user_scripts_->erase(iter);
478 } else {
479 ++iter;
480 }
481 }
482
483 // Add any content scripts for extensions that were recently loaded.
484 const ExtensionSet& enabled_extensions =
485 ExtensionRegistry::Get(profile_)->enabled_extensions();
486 for (std::set<std::string>::const_iterator iter = added_extensions_.begin();
487 iter != added_extensions_.end(); ++iter) {
488 const Extension* extension = enabled_extensions.GetByID(*iter);
489 if (!extension)
490 continue;
491 bool incognito_enabled =
492 util::IsIncognitoEnabled(extension->id(), profile_);
493 const UserScriptList& scripts =
494 ContentScriptsInfo::GetContentScripts(extension);
495 for (UserScriptList::const_iterator script = scripts.begin();
496 script != scripts.end();
497 ++script) {
498 user_scripts_->push_back(*script);
499 user_scripts_->back().set_incognito_enabled(incognito_enabled);
500 }
501 }
502
503 BrowserThread::PostTask(
504 BrowserThread::FILE,
505 FROM_HERE,
506 base::Bind(&LoadScriptsOnFileThread,
507 base::Passed(&user_scripts_),
508 extensions_info_,
509 added_extensions_,
510 make_scoped_refptr(
511 ExtensionSystem::Get(profile_)->content_verifier()),
512 base::Bind(&UserScriptMaster::OnScriptsLoaded,
513 weak_factory_.GetWeakPtr())));
514 added_extensions_.clear();
515 removed_extensions_.clear();
516 user_scripts_.reset(NULL);
502 } 517 }
503 518
504 void UserScriptMaster::SendUpdate( 519 void UserScriptMaster::SendUpdate(
505 content::RenderProcessHost* process, 520 content::RenderProcessHost* process,
506 base::SharedMemory* shared_memory, 521 base::SharedMemory* shared_memory,
507 const std::set<std::string>& changed_extensions) { 522 const std::set<std::string>& changed_extensions) {
508 // Don't allow injection of content scripts into <webview>. 523 // Don't allow injection of content scripts into <webview>.
509 if (process->IsIsolatedGuest()) 524 if (process->IsIsolatedGuest())
510 return; 525 return;
511 526
(...skipping 12 matching lines...) Expand all
524 if (!shared_memory->ShareToProcess(handle, &handle_for_process)) 539 if (!shared_memory->ShareToProcess(handle, &handle_for_process))
525 return; // This can legitimately fail if the renderer asserts at startup. 540 return; // This can legitimately fail if the renderer asserts at startup.
526 541
527 if (base::SharedMemory::IsHandleValid(handle_for_process)) { 542 if (base::SharedMemory::IsHandleValid(handle_for_process)) {
528 process->Send(new ExtensionMsg_UpdateUserScripts(handle_for_process, 543 process->Send(new ExtensionMsg_UpdateUserScripts(handle_for_process,
529 changed_extensions)); 544 changed_extensions));
530 } 545 }
531 } 546 }
532 547
533 } // namespace extensions 548 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/user_script_master.h ('k') | chrome/browser/extensions/user_script_master_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698