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 3// found in the LICENSE file. 4 5#include "extensions/renderer/user_script_set.h" 6 7#include "content/public/common/url_constants.h" 8#include "content/public/renderer/render_thread.h" 9#include "extensions/common/extension.h" 10#include "extensions/common/extension_set.h" 11#include "extensions/common/permissions/permissions_data.h" 12#include "extensions/renderer/extensions_renderer_client.h" 13#include "extensions/renderer/script_context.h" 14#include "extensions/renderer/script_injection.h" 15#include "extensions/renderer/user_script_injector.h" 16#include "third_party/WebKit/public/web/WebDocument.h" 17#include "third_party/WebKit/public/web/WebFrame.h" 18#include "url/gurl.h" 19 20namespace extensions { 21 22namespace { 23 24GURL GetDocumentUrlForFrame(blink::WebFrame* frame) { 25 GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); 26 if (!data_source_url.is_empty() && frame->isViewSourceModeEnabled()) { 27 data_source_url = GURL(content::kViewSourceScheme + std::string(":") + 28 data_source_url.spec()); 29 } 30 31 return data_source_url; 32} 33 34} // namespace 35 36UserScriptSet::UserScriptSet(const ExtensionSet* extensions) 37 : extensions_(extensions) { 38} 39 40UserScriptSet::~UserScriptSet() { 41} 42 43void UserScriptSet::AddObserver(Observer* observer) { 44 observers_.AddObserver(observer); 45} 46 47void UserScriptSet::RemoveObserver(Observer* observer) { 48 observers_.RemoveObserver(observer); 49} 50 51void UserScriptSet::GetActiveExtensionIds( 52 std::set<std::string>* ids) const { 53 for (ScopedVector<UserScript>::const_iterator iter = scripts_.begin(); 54 iter != scripts_.end(); 55 ++iter) { 56 DCHECK(!(*iter)->extension_id().empty()); 57 ids->insert((*iter)->extension_id()); 58 } 59} 60 61void UserScriptSet::GetInjections( 62 ScopedVector<ScriptInjection>* injections, 63 blink::WebFrame* web_frame, 64 int tab_id, 65 UserScript::RunLocation run_location) { 66 GURL document_url = GetDocumentUrlForFrame(web_frame); 67 for (ScopedVector<UserScript>::const_iterator iter = scripts_.begin(); 68 iter != scripts_.end(); 69 ++iter) { 70 const Extension* extension = extensions_->GetByID((*iter)->extension_id()); 71 if (!extension) 72 continue; 73 scoped_ptr<ScriptInjection> injection = GetInjectionForScript( 74 *iter, 75 web_frame, 76 tab_id, 77 run_location, 78 document_url, 79 extension, 80 false /* is_declarative */); 81 if (injection.get()) 82 injections->push_back(injection.release()); 83 } 84} 85 86bool UserScriptSet::UpdateUserScripts( 87 base::SharedMemoryHandle shared_memory, 88 const std::set<std::string>& changed_extensions) { 89 bool only_inject_incognito = 90 ExtensionsRendererClient::Get()->IsIncognitoProcess(); 91 92 // Create the shared memory object (read only). 93 shared_memory_.reset(new base::SharedMemory(shared_memory, true)); 94 if (!shared_memory_.get()) 95 return false; 96 97 // First get the size of the memory block. 98 if (!shared_memory_->Map(sizeof(Pickle::Header))) 99 return false; 100 Pickle::Header* pickle_header = 101 reinterpret_cast<Pickle::Header*>(shared_memory_->memory()); 102 103 // Now map in the rest of the block. 104 int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size; 105 shared_memory_->Unmap(); 106 if (!shared_memory_->Map(pickle_size)) 107 return false; 108 109 // Unpickle scripts. 110 uint64 num_scripts = 0; 111 Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()), pickle_size); 112 PickleIterator iter(pickle); 113 CHECK(pickle.ReadUInt64(&iter, &num_scripts)); 114 115 scripts_.clear(); 116 scripts_.reserve(num_scripts); 117 for (uint64 i = 0; i < num_scripts; ++i) { 118 scoped_ptr<UserScript> script(new UserScript()); 119 script->Unpickle(pickle, &iter); 120 121 // Note that this is a pointer into shared memory. We don't own it. It gets 122 // cleared up when the last renderer or browser process drops their 123 // reference to the shared memory. 124 for (size_t j = 0; j < script->js_scripts().size(); ++j) { 125 const char* body = NULL; 126 int body_length = 0; 127 CHECK(pickle.ReadData(&iter, &body, &body_length)); 128 script->js_scripts()[j].set_external_content( 129 base::StringPiece(body, body_length)); 130 } 131 for (size_t j = 0; j < script->css_scripts().size(); ++j) { 132 const char* body = NULL; 133 int body_length = 0; 134 CHECK(pickle.ReadData(&iter, &body, &body_length)); 135 script->css_scripts()[j].set_external_content( 136 base::StringPiece(body, body_length)); 137 } 138 139 if (only_inject_incognito && !script->is_incognito_enabled()) 140 continue; // This script shouldn't run in an incognito tab. 141 142 scripts_.push_back(script.release()); 143 } 144 145 FOR_EACH_OBSERVER(Observer, 146 observers_, 147 OnUserScriptsUpdated(changed_extensions, scripts_.get())); 148 return true; 149} 150 151scoped_ptr<ScriptInjection> UserScriptSet::GetDeclarativeScriptInjection( 152 int script_id, 153 blink::WebFrame* web_frame, 154 int tab_id, 155 UserScript::RunLocation run_location, 156 const GURL& document_url, 157 const Extension* extension) { 158 for (ScopedVector<UserScript>::const_iterator it = scripts_.begin(); 159 it != scripts_.end(); 160 ++it) { 161 if ((*it)->id() == script_id) { 162 return GetInjectionForScript(*it, 163 web_frame, 164 tab_id, 165 run_location, 166 document_url, 167 extension, 168 true /* is_declarative */); 169 } 170 } 171 return scoped_ptr<ScriptInjection>(); 172} 173 174// TODO(dcheng): Scripts can't be injected on a remote frame, so this function 175// signature needs to be updated. 176scoped_ptr<ScriptInjection> UserScriptSet::GetInjectionForScript( 177 UserScript* script, 178 blink::WebFrame* web_frame, 179 int tab_id, 180 UserScript::RunLocation run_location, 181 const GURL& document_url, 182 const Extension* extension, 183 bool is_declarative) { 184 scoped_ptr<ScriptInjection> injection; 185 if (web_frame->parent() && !script->match_all_frames()) 186 return injection.Pass(); // Only match subframes if the script declared it. 187 188 GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL( 189 web_frame, document_url, script->match_about_blank()); 190 191 if (!script->MatchesURL(effective_document_url)) 192 return injection.Pass(); 193 194 scoped_ptr<ScriptInjector> injector(new UserScriptInjector(script, 195 this, 196 is_declarative)); 197 if (injector->CanExecuteOnFrame( 198 extension, 199 web_frame, 200 -1, // Content scripts are not tab-specific. 201 web_frame->top()->document().url()) == 202 PermissionsData::ACCESS_DENIED) { 203 return injection.Pass(); 204 } 205 206 bool inject_css = !script->css_scripts().empty() && 207 run_location == UserScript::DOCUMENT_START; 208 bool inject_js = 209 !script->js_scripts().empty() && script->run_location() == run_location; 210 if (inject_css || inject_js) { 211 injection.reset(new ScriptInjection( 212 injector.Pass(), 213 web_frame->toWebLocalFrame(), 214 extension->id(), 215 run_location, 216 tab_id)); 217 } 218 return injection.Pass(); 219} 220 221} // namespace extensions 222