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