// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/renderer/user_script_set.h" #include "content/public/common/url_constants.h" #include "content/public/renderer/render_thread.h" #include "extensions/common/extension.h" #include "extensions/common/extension_set.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/renderer/extensions_renderer_client.h" #include "extensions/renderer/script_context.h" #include "extensions/renderer/script_injection.h" #include "extensions/renderer/user_script_injector.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "url/gurl.h" namespace extensions { namespace { GURL GetDocumentUrlForFrame(blink::WebFrame* frame) { GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); if (!data_source_url.is_empty() && frame->isViewSourceModeEnabled()) { data_source_url = GURL(content::kViewSourceScheme + std::string(":") + data_source_url.spec()); } return data_source_url; } } // namespace UserScriptSet::UserScriptSet(const ExtensionSet* extensions) : extensions_(extensions) { } UserScriptSet::~UserScriptSet() { } void UserScriptSet::AddObserver(Observer* observer) { observers_.AddObserver(observer); } void UserScriptSet::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } void UserScriptSet::GetActiveExtensionIds( std::set<std::string>* ids) const { for (ScopedVector<UserScript>::const_iterator iter = scripts_.begin(); iter != scripts_.end(); ++iter) { DCHECK(!(*iter)->extension_id().empty()); ids->insert((*iter)->extension_id()); } } void UserScriptSet::GetInjections( ScopedVector<ScriptInjection>* injections, blink::WebFrame* web_frame, int tab_id, UserScript::RunLocation run_location) { GURL document_url = GetDocumentUrlForFrame(web_frame); for (ScopedVector<UserScript>::const_iterator iter = scripts_.begin(); iter != scripts_.end(); ++iter) { const Extension* extension = extensions_->GetByID((*iter)->extension_id()); if (!extension) continue; scoped_ptr<ScriptInjection> injection = GetInjectionForScript( *iter, web_frame, tab_id, run_location, document_url, extension, false /* is_declarative */); if (injection.get()) injections->push_back(injection.release()); } } bool UserScriptSet::UpdateUserScripts( base::SharedMemoryHandle shared_memory, const std::set<std::string>& changed_extensions) { bool only_inject_incognito = ExtensionsRendererClient::Get()->IsIncognitoProcess(); // Create the shared memory object (read only). shared_memory_.reset(new base::SharedMemory(shared_memory, true)); if (!shared_memory_.get()) return false; // First get the size of the memory block. if (!shared_memory_->Map(sizeof(Pickle::Header))) return false; Pickle::Header* pickle_header = reinterpret_cast<Pickle::Header*>(shared_memory_->memory()); // Now map in the rest of the block. int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size; shared_memory_->Unmap(); if (!shared_memory_->Map(pickle_size)) return false; // Unpickle scripts. uint64 num_scripts = 0; Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()), pickle_size); PickleIterator iter(pickle); CHECK(pickle.ReadUInt64(&iter, &num_scripts)); scripts_.clear(); scripts_.reserve(num_scripts); for (uint64 i = 0; i < num_scripts; ++i) { scoped_ptr<UserScript> script(new UserScript()); script->Unpickle(pickle, &iter); // Note that this is a pointer into shared memory. We don't own it. It gets // cleared up when the last renderer or browser process drops their // reference to the shared memory. for (size_t j = 0; j < script->js_scripts().size(); ++j) { const char* body = NULL; int body_length = 0; CHECK(pickle.ReadData(&iter, &body, &body_length)); script->js_scripts()[j].set_external_content( base::StringPiece(body, body_length)); } for (size_t j = 0; j < script->css_scripts().size(); ++j) { const char* body = NULL; int body_length = 0; CHECK(pickle.ReadData(&iter, &body, &body_length)); script->css_scripts()[j].set_external_content( base::StringPiece(body, body_length)); } if (only_inject_incognito && !script->is_incognito_enabled()) continue; // This script shouldn't run in an incognito tab. scripts_.push_back(script.release()); } FOR_EACH_OBSERVER(Observer, observers_, OnUserScriptsUpdated(changed_extensions, scripts_.get())); return true; } scoped_ptr<ScriptInjection> UserScriptSet::GetDeclarativeScriptInjection( int script_id, blink::WebFrame* web_frame, int tab_id, UserScript::RunLocation run_location, const GURL& document_url, const Extension* extension) { for (ScopedVector<UserScript>::const_iterator it = scripts_.begin(); it != scripts_.end(); ++it) { if ((*it)->id() == script_id) { return GetInjectionForScript(*it, web_frame, tab_id, run_location, document_url, extension, true /* is_declarative */); } } return scoped_ptr<ScriptInjection>(); } // TODO(dcheng): Scripts can't be injected on a remote frame, so this function // signature needs to be updated. scoped_ptr<ScriptInjection> UserScriptSet::GetInjectionForScript( UserScript* script, blink::WebFrame* web_frame, int tab_id, UserScript::RunLocation run_location, const GURL& document_url, const Extension* extension, bool is_declarative) { scoped_ptr<ScriptInjection> injection; if (web_frame->parent() && !script->match_all_frames()) return injection.Pass(); // Only match subframes if the script declared it. GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL( web_frame, document_url, script->match_about_blank()); if (!script->MatchesURL(effective_document_url)) return injection.Pass(); scoped_ptr<ScriptInjector> injector(new UserScriptInjector(script, this, is_declarative)); if (injector->CanExecuteOnFrame( extension, web_frame, -1, // Content scripts are not tab-specific. web_frame->top()->document().url()) == PermissionsData::ACCESS_DENIED) { return injection.Pass(); } bool inject_css = !script->css_scripts().empty() && run_location == UserScript::DOCUMENT_START; bool inject_js = !script->js_scripts().empty() && script->run_location() == run_location; if (inject_css || inject_js) { injection.reset(new ScriptInjection( injector.Pass(), web_frame->toWebLocalFrame(), extension->id(), run_location, tab_id)); } return injection.Pass(); } } // namespace extensions