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 20 namespace extensions { 21 22 namespace { 23 24 GURL 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 36 UserScriptSet::UserScriptSet(const ExtensionSet* extensions) 37 : extensions_(extensions) { 38 } 39 40 UserScriptSet::~UserScriptSet() { 41 } 42 43 void UserScriptSet::AddObserver(Observer* observer) { 44 observers_.AddObserver(observer); 45 } 46 47 void UserScriptSet::RemoveObserver(Observer* observer) { 48 observers_.RemoveObserver(observer); 49 } 50 51 void 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 61 void 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 86 bool 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 151 scoped_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. 176 scoped_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