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