16e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/extensions/user_script_loader.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include <set> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/bind_helpers.h" 122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h" 131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h" 14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/memory/shared_memory.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/version.h" 167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h" 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/api/i18n/default_locale_handler.h" 196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_process_host.h" 226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "extensions/browser/component_extension_resource_manager.h" 23010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "extensions/browser/content_verifier.h" 240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "extensions/browser/extension_registry.h" 25010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "extensions/browser/extension_system.h" 266d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "extensions/browser/extensions_browser_client.h" 276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "extensions/common/extension_messages.h" 28c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "extensions/common/file_util.h" 29c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "extensions/common/message_bundle.h" 306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "extensions/common/one_shot_event.h" 31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/base/resource/resource_bundle.h" 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread; 346d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)using extensions::ExtensionsBrowserClient; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions { 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 38116680a4aac90f2aa7413d9095a592090648e557Ben Murdochnamespace { 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)typedef base::Callback< 416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) void(scoped_ptr<UserScriptList>, scoped_ptr<base::SharedMemory>)> 426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) LoadScriptsCallback; 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 44116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid VerifyContent(scoped_refptr<ContentVerifier> verifier, 456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const ExtensionId& extension_id, 46116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const base::FilePath& extension_root, 47116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const base::FilePath& relative_path, 48116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const std::string& content) { 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 50010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) scoped_refptr<ContentVerifyJob> job( 51010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) verifier->CreateJobFor(extension_id, extension_root, relative_path)); 52010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (job.get()) { 53010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) job->Start(); 54010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) job->BytesRead(content.size(), content.data()); 55010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) job->DoneReading(); 56010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)} 58010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool LoadScriptContent(const ExtensionId& extension_id, 60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch UserScript::File* script_file, 61116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const SubstitutionMap* localization_messages, 62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch scoped_refptr<ContentVerifier> verifier) { 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string content; 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& path = ExtensionResource::GetFilePath( 656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) script_file->extension_root(), 666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) script_file->relative_path(), 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT); 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (path.empty()) { 69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int resource_id; 706d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) if (ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()-> 716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) IsComponentExtensionResource(script_file->extension_root(), 726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) script_file->relative_path(), 736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) &resource_id)) { 74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) content = rb.GetRawDataResource(resource_id).as_string(); 76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } else { 77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) LOG(WARNING) << "Failed to get file path to " 78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) << script_file->relative_path().value() << " from " 79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) << script_file->extension_root().value(); 80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return false; 81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } else { 8358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) if (!base::ReadFileToString(path, &content)) { 84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) LOG(WARNING) << "Failed to load user script file: " << path.value(); 85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return false; 86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (verifier.get()) { 885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content::BrowserThread::PostTask(content::BrowserThread::IO, 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) FROM_HERE, 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::Bind(&VerifyContent, 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) verifier, 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extension_id, 935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) script_file->extension_root(), 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) script_file->relative_path(), 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content)); 96010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Localize the content. 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (localization_messages) { 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string error; 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MessageBundle::ReplaceMessagesWithExternalDictionary( 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *localization_messages, &content, &error); 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!error.empty()) { 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Failed to replace messages in script: " << error; 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Remove BOM from the content. 110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) std::string::size_type index = content.find(base::kUtf8ByteOrderMark); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (index == 0) { 112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark))); 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) script_file->set_content(content); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 120116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSubstitutionMap* GetLocalizationMessages(const ExtensionsInfo& extensions_info, 1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const ExtensionId& extension_id) { 122116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionsInfo::const_iterator iter = extensions_info.find(extension_id); 123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (iter == extensions_info.end()) 124116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return NULL; 1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return file_util::LoadMessageBundleSubstitutionMap( 1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) iter->second.first, extension_id, iter->second.second); 127116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch} 128116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 129116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid LoadUserScripts(UserScriptList* user_scripts, 130116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const ExtensionsInfo& extensions_info, 13103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) const std::set<int>& added_script_ids, 132116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ContentVerifier* verifier) { 1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (UserScriptList::iterator script = user_scripts->begin(); 1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) script != user_scripts->end(); 1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++script) { 1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (added_script_ids.count(script->id()) == 0) 137116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch continue; 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<SubstitutionMap> localization_messages( 1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) GetLocalizationMessages(extensions_info, script->extension_id())); 1406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (size_t k = 0; k < script->js_scripts().size(); ++k) { 1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) UserScript::File& script_file = script->js_scripts()[k]; 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (script_file.GetContent().empty()) 1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) LoadScriptContent(script->extension_id(), &script_file, NULL, verifier); 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (size_t k = 0; k < script->css_scripts().size(); ++k) { 1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) UserScript::File& script_file = script->css_scripts()[k]; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (script_file.GetContent().empty()) 1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) LoadScriptContent(script->extension_id(), 149010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) &script_file, 150010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) localization_messages.get(), 151116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch verifier); 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Pickle user scripts and return pointer to the shared memory. 157116680a4aac90f2aa7413d9095a592090648e557Ben Murdochscoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) { 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Pickle pickle; 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pickle.WriteUInt64(scripts.size()); 1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (UserScriptList::const_iterator script = scripts.begin(); 1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) script != scripts.end(); 1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++script) { 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(aa): This can be replaced by sending content script metadata to 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // renderers along with other extension data in ExtensionMsg_Loaded. 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // See crbug.com/70516. 1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) script->Pickle(&pickle); 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Write scripts as 'data' so that we can read it out in the slave without 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // allocating a new string. 1696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (size_t j = 0; j < script->js_scripts().size(); j++) { 1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::StringPiece contents = script->js_scripts()[j].GetContent(); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pickle.WriteData(contents.data(), contents.length()); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (size_t j = 0; j < script->css_scripts().size(); j++) { 1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::StringPiece contents = script->css_scripts()[j].GetContent(); 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pickle.WriteData(contents.data(), contents.length()); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create the shared memory object. 180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::SharedMemory shared_memory; 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch base::SharedMemoryCreateOptions options; 1830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch options.size = pickle.size(); 1840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch options.share_read_only = true; 1850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if (!shared_memory.Create(options)) 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return scoped_ptr<base::SharedMemory>(); 1870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if (!shared_memory.Map(pickle.size())) 189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return scoped_ptr<base::SharedMemory>(); 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Copy the pickle to shared memory. 192f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) memcpy(shared_memory.memory(), pickle.data(), pickle.size()); 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::SharedMemoryHandle readonly_handle; 195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(), 196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) &readonly_handle)) 197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return scoped_ptr<base::SharedMemory>(); 198f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return make_scoped_ptr(new base::SharedMemory(readonly_handle, 200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /*read_only=*/true)); 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 203116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid LoadScriptsOnFileThread(scoped_ptr<UserScriptList> user_scripts, 204116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const ExtensionsInfo& extensions_info, 20503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) const std::set<int>& added_script_ids, 206116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch scoped_refptr<ContentVerifier> verifier, 207116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch LoadScriptsCallback callback) { 208116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK(user_scripts.get()); 209116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch LoadUserScripts( 2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci user_scripts.get(), extensions_info, added_script_ids, verifier.get()); 211116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts); 212116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch BrowserThread::PostTask( 213116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch BrowserThread::UI, 214116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch FROM_HERE, 2156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory))); 216116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch} 217116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 218116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Helper function to parse greasesmonkey headers 219116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool GetDeclarationValue(const base::StringPiece& line, 220116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const base::StringPiece& prefix, 221116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch std::string* value) { 222116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::StringPiece::size_type index = line.find(prefix); 223116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (index == base::StringPiece::npos) 224116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return false; 225116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 226116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch std::string temp(line.data() + index + prefix.length(), 227116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch line.length() - index - prefix.length()); 228116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 229116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (temp.empty() || !IsWhitespace(temp[0])) 230116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return false; 231116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 232116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value); 233116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return true; 234116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch} 235116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 236116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch} // namespace 237116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 238116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// static 2396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool UserScriptLoader::ParseMetadataHeader(const base::StringPiece& script_text, 2406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) UserScript* script) { 241116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // http://wiki.greasespot.net/Metadata_block 242116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::StringPiece line; 243116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch size_t line_start = 0; 244116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch size_t line_end = line_start; 245116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch bool in_metadata = false; 246116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 247116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kUserScriptBegin("// ==UserScript=="); 248116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kUserScriptEng("// ==/UserScript=="); 249116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kNamespaceDeclaration("// @namespace"); 250116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kNameDeclaration("// @name"); 251116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kVersionDeclaration("// @version"); 252116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kDescriptionDeclaration("// @description"); 253116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kIncludeDeclaration("// @include"); 254116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kExcludeDeclaration("// @exclude"); 255116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kMatchDeclaration("// @match"); 256116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kExcludeMatchDeclaration("// @exclude_match"); 257116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kRunAtDeclaration("// @run-at"); 258116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kRunAtDocumentStartValue("document-start"); 259116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kRunAtDocumentEndValue("document-end"); 260116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch static const base::StringPiece kRunAtDocumentIdleValue("document-idle"); 261116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 262116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch while (line_start < script_text.length()) { 263116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch line_end = script_text.find('\n', line_start); 264116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 265116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Handle the case where there is no trailing newline in the file. 266116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (line_end == std::string::npos) 267116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch line_end = script_text.length() - 1; 268116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 269116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch line.set(script_text.data() + line_start, line_end - line_start); 270116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 271116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!in_metadata) { 272116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (line.starts_with(kUserScriptBegin)) 273116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch in_metadata = true; 274116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else { 275116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (line.starts_with(kUserScriptEng)) 276116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch break; 277116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 278116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch std::string value; 279116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (GetDeclarationValue(line, kIncludeDeclaration, &value)) { 280116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // We escape some characters that MatchPattern() considers special. 281116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\"); 282116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?"); 283116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->add_glob(value); 284116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (GetDeclarationValue(line, kExcludeDeclaration, &value)) { 285116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\"); 286116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?"); 287116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->add_exclude_glob(value); 288116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (GetDeclarationValue(line, kNamespaceDeclaration, &value)) { 289116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->set_name_space(value); 290116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (GetDeclarationValue(line, kNameDeclaration, &value)) { 291116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->set_name(value); 292116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (GetDeclarationValue(line, kVersionDeclaration, &value)) { 293116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Version version(value); 294116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (version.IsValid()) 295116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->set_version(version.GetString()); 296116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) { 297116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->set_description(value); 298116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) { 299116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch URLPattern pattern(UserScript::ValidUserScriptSchemes()); 300116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (URLPattern::PARSE_SUCCESS != pattern.Parse(value)) 301116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return false; 302116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->add_url_pattern(pattern); 303116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (GetDeclarationValue(line, kExcludeMatchDeclaration, &value)) { 304116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch URLPattern exclude(UserScript::ValidUserScriptSchemes()); 305116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (URLPattern::PARSE_SUCCESS != exclude.Parse(value)) 306116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return false; 307116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->add_exclude_url_pattern(exclude); 308116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) { 309116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (value == kRunAtDocumentStartValue) 310116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->set_run_location(UserScript::DOCUMENT_START); 311116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch else if (value == kRunAtDocumentEndValue) 312116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->set_run_location(UserScript::DOCUMENT_END); 313116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch else if (value == kRunAtDocumentIdleValue) 314116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->set_run_location(UserScript::DOCUMENT_IDLE); 315116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch else 316116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return false; 317116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 318116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 319116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // TODO(aa): Handle more types of metadata. 320116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 321116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 322116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch line_start = line_end + 1; 323116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 324116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 325116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // If no patterns were specified, default to @include *. This is what 326116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Greasemonkey does. 327116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (script->globs().empty() && script->url_patterns().is_empty()) 328116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch script->add_glob("*"); 329116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 330116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return true; 331116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch} 332116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 333116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// static 3346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::LoadScriptsForTest(UserScriptList* user_scripts) { 335116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionsInfo info; 33603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) std::set<int> added_script_ids; 3376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (UserScriptList::iterator it = user_scripts->begin(); 3386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) it != user_scripts->end(); 3396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++it) { 3406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) added_script_ids.insert(it->id()); 341116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 342116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch LoadUserScripts( 3436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) user_scripts, info, added_script_ids, NULL /* no verifier for testing */); 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UserScriptLoader::UserScriptLoader(Profile* profile, 3476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const ExtensionId& owner_extension_id, 3486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) bool listen_for_extension_system_loaded) 349116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch : user_scripts_(new UserScriptList()), 35003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) clear_scripts_(false), 3516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) extension_system_ready_(false), 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_load_(false), 3530529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch profile_(profile), 3546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) owner_extension_id_(owner_extension_id), 3551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci extension_registry_observer_(this), 3561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci weak_factory_(this) { 3576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) extension_registry_observer_.Add(ExtensionRegistry::Get(profile)); 3586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (listen_for_extension_system_loaded) { 3596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ExtensionSystem::Get(profile_)->ready().Post( 3606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) FROM_HERE, 3616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::Bind(&UserScriptLoader::OnExtensionSystemReady, 3626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) weak_factory_.GetWeakPtr())); 3636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else { 3646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) extension_system_ready_ = true; 3656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) registrar_.Add(this, 3676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) content::NOTIFICATION_RENDERER_PROCESS_CREATED, 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content::NotificationService::AllBrowserContextsAndSources()); 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UserScriptLoader::~UserScriptLoader() { 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) { 3756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (std::set<UserScript>::const_iterator it = scripts.begin(); 3766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) it != scripts.end(); 3776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++it) { 3786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) removed_scripts_.erase(*it); 3796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) added_scripts_.insert(*it); 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) AttemptLoad(); 382010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)} 383010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 3846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) { 3856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (std::set<UserScript>::const_iterator it = scripts.begin(); 3866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) it != scripts.end(); 3876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++it) { 3886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) added_scripts_.erase(*it); 3896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) removed_scripts_.insert(*it); 3900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch } 3916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) AttemptLoad(); 3920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch} 3930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 3946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::ClearScripts() { 3956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) clear_scripts_ = true; 3966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) added_scripts_.clear(); 3976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) removed_scripts_.clear(); 3986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) AttemptLoad(); 3990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch} 4000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 4016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::Observe(int type, 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const content::NotificationSource& source, 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const content::NotificationDetails& details) { 4045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED); 4055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content::RenderProcessHost* process = 4065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content::Source<content::RenderProcessHost>(source).ptr(); 4076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext()); 4085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!profile_->IsSameProfile(profile)) 4095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return; 4106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (scripts_ready()) { 4115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) SendUpdate(process, 4126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) shared_memory_.get(), 4136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) std::set<ExtensionId>()); // Include all extensions. 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::OnExtensionUnloaded( 4186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) content::BrowserContext* browser_context, 4196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const Extension* extension, 4206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) UnloadedExtensionInfo::Reason reason) { 4216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) extensions_info_.erase(extension->id()); 4226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 4236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::OnExtensionSystemReady() { 4256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) extension_system_ready_ = true; 4266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) AttemptLoad(); 4276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 4286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool UserScriptLoader::ScriptsMayHaveChanged() const { 4306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Scripts may have changed if there are scripts added, scripts removed, or 4316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // if scripts were cleared and either: 4326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // (1) A load is in progress (which may result in a non-zero number of 4336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // scripts that need to be cleared), or 4346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // (2) The current set of scripts is non-empty (so they need to be cleared). 4356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return (added_scripts_.size() || 4366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) removed_scripts_.size() || 4376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) (clear_scripts_ && 4386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) (is_loading() || user_scripts_->size()))); 4396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 4406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::AttemptLoad() { 4426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (extension_system_ready_ && ScriptsMayHaveChanged()) { 4436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (is_loading()) 4446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) pending_load_ = true; 4456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) else 4466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) StartLoad(); 4476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::StartLoad() { 451116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK_CURRENTLY_ON(BrowserThread::UI); 452116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK(!is_loading()); 453116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 4546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // If scripts were marked for clearing before adding and removing, then clear 4556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // them. 4566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (clear_scripts_) { 4576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) user_scripts_->clear(); 4586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else { 4596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (UserScriptList::iterator it = user_scripts_->begin(); 4606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) it != user_scripts_->end();) { 4616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (removed_scripts_.count(*it)) 4626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) it = user_scripts_->erase(it); 4636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) else 4646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++it; 465116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 466116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) user_scripts_->insert( 4696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) user_scripts_->end(), added_scripts_.begin(), added_scripts_.end()); 4706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 47103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) std::set<int> added_script_ids; 4726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (std::set<UserScript>::const_iterator it = added_scripts_.begin(); 4736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) it != added_scripts_.end(); 4746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++it) { 4756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) added_script_ids.insert(it->id()); 476116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 477116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 4786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Expand |changed_extensions_| for OnScriptsLoaded, which will use it in 4796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // its IPC message. This must be done before we clear |added_scripts_| and 4806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // |removed_scripts_| below. 4816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) std::set<UserScript> changed_scripts(added_scripts_); 4826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) changed_scripts.insert(removed_scripts_.begin(), removed_scripts_.end()); 4836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ExpandChangedExtensions(changed_scripts); 4846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Update |extensions_info_| to contain info from every extension in 4866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // |changed_extensions_| before passing it to LoadScriptsOnFileThread. 4876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) UpdateExtensionsInfo(); 4886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 489116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch BrowserThread::PostTask( 490116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch BrowserThread::FILE, 491116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch FROM_HERE, 492116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::Bind(&LoadScriptsOnFileThread, 493116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::Passed(&user_scripts_), 494116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch extensions_info_, 4956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) added_script_ids, 496116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch make_scoped_refptr( 497116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionSystem::Get(profile_)->content_verifier()), 4986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::Bind(&UserScriptLoader::OnScriptsLoaded, 499116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch weak_factory_.GetWeakPtr()))); 5006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) clear_scripts_ = false; 5026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) added_scripts_.clear(); 5036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) removed_scripts_.clear(); 504116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch user_scripts_.reset(NULL); 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::OnScriptsLoaded( 5086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) scoped_ptr<UserScriptList> user_scripts, 5096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) scoped_ptr<base::SharedMemory> shared_memory) { 5106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) user_scripts_.reset(user_scripts.release()); 5116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (pending_load_) { 5126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // While we were loading, there were further changes. Don't bother 5136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // notifying about these scripts and instead just immediately reload. 5146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) pending_load_ = false; 5156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) StartLoad(); 5166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 5176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 5186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (shared_memory.get() == NULL) { 5206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // This can happen if we run out of file descriptors. In that case, we 5216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // have a choice between silently omitting all user scripts for new tabs, 5226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // by nulling out shared_memory_, or only silently omitting new ones by 5236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // leaving the existing object in place. The second seems less bad, even 5246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // though it removes the possibility that freeing the shared memory block 5256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // would open up enough FDs for long enough for a retry to succeed. 5266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Pretend the extension change didn't happen. 5286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 5296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 5306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // We've got scripts ready to go. 5326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) shared_memory_.reset(shared_memory.release()); 5336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (content::RenderProcessHost::iterator i( 5356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) content::RenderProcessHost::AllHostsIterator()); 5366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) !i.IsAtEnd(); 5376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) i.Advance()) { 5386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) SendUpdate(i.GetCurrentValue(), shared_memory_.get(), changed_extensions_); 5396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 5406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) changed_extensions_.clear(); 5416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) content::NotificationService::current()->Notify( 5436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) extensions::NOTIFICATION_USER_SCRIPTS_UPDATED, 5446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) content::Source<Profile>(profile_), 5456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) content::Details<base::SharedMemory>(shared_memory_.get())); 5466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 5476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::SendUpdate( 549cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) content::RenderProcessHost* process, 550cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::SharedMemory* shared_memory, 5516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const std::set<ExtensionId>& changed_extensions) { 552b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) // Don't allow injection of content scripts into <webview>. 553cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (process->IsIsolatedGuest()) 554b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return; 555b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext()); 5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Make sure we only send user scripts to processes in our profile. 5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!profile_->IsSameProfile(profile)) 5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the process is being started asynchronously, early return. We'll end up 5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // calling InitUserScripts when it's created which will call this again. 5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::ProcessHandle handle = process->GetHandle(); 5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!handle) 5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SharedMemoryHandle handle_for_process; 5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!shared_memory->ShareToProcess(handle, &handle_for_process)) 5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; // This can legitimately fail if the renderer asserts at startup. 5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 571cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (base::SharedMemory::IsHandleValid(handle_for_process)) { 5725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) process->Send(new ExtensionMsg_UpdateUserScripts( 5731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci handle_for_process, owner_extension_id_, changed_extensions)); 574cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::ExpandChangedExtensions( 5786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const std::set<UserScript>& scripts) { 5796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (std::set<UserScript>::const_iterator it = scripts.begin(); 5806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) it != scripts.end(); 5816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++it) { 5826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) changed_extensions_.insert(it->extension_id()); 5836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 5846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 5856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void UserScriptLoader::UpdateExtensionsInfo() { 5876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); 5886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (std::set<ExtensionId>::const_iterator it = changed_extensions_.begin(); 5896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) it != changed_extensions_.end(); 5906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++it) { 5916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (extensions_info_.find(*it) == extensions_info_.end()) { 5926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const Extension* extension = 5936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) registry->GetExtensionById(*it, ExtensionRegistry::EVERYTHING); 5946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // |changed_extensions_| may include extensions that have been removed, 5956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // which leads to the above lookup failing. In this case, just continue. 5966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!extension) 5976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) continue; 5986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) extensions_info_[*it] = ExtensionSet::ExtensionPathAndDefaultLocale( 5996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) extension->path(), LocaleInfo::GetDefaultLocale(extension)); 6006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 6016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 6026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 6036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace extensions 605