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