// 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_injector.h" #include #include "base/lazy_instance.h" #include "content/public/common/url_constants.h" #include "extensions/common/extension.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/renderer/script_context.h" #include "extensions/renderer/scripts_run_info.h" #include "grit/extensions_renderer_resources.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "ui/base/resource/resource_bundle.h" #include "url/gurl.h" namespace extensions { namespace { // These two strings are injected before and after the Greasemonkey API and // user script to wrap it in an anonymous scope. const char kUserScriptHead[] = "(function (unsafeWindow) {\n"; const char kUserScriptTail[] = "\n})(window);"; // Greasemonkey API source that is injected with the scripts. struct GreasemonkeyApiJsString { GreasemonkeyApiJsString(); blink::WebScriptSource GetSource() const; private: std::string source_; }; // The below constructor, monstrous as it is, just makes a WebScriptSource from // the GreasemonkeyApiJs resource. GreasemonkeyApiJsString::GreasemonkeyApiJsString() : source_(ResourceBundle::GetSharedInstance() .GetRawDataResource(IDR_GREASEMONKEY_API_JS) .as_string()) { } blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const { return blink::WebScriptSource(blink::WebString::fromUTF8(source_)); } base::LazyInstance g_greasemonkey_api = LAZY_INSTANCE_INITIALIZER; } // namespace UserScriptInjector::UserScriptInjector( const UserScript* script, UserScriptSet* script_list, bool is_declarative) : script_(script), script_id_(script_->id()), extension_id_(script_->extension_id()), is_declarative_(is_declarative), user_script_set_observer_(this) { user_script_set_observer_.Add(script_list); } UserScriptInjector::~UserScriptInjector() { } void UserScriptInjector::OnUserScriptsUpdated( const std::set& changed_extensions, const std::vector& scripts) { // If the extension causing this injection changed, then this injection // will be removed, and there's no guarantee the backing script still exists. if (changed_extensions.count(extension_id_) > 0) return; for (std::vector::const_iterator iter = scripts.begin(); iter != scripts.end(); ++iter) { // We need to compare to |script_id_| (and not to script_->id()) because the // old |script_| may be deleted by now. if ((*iter)->id() == script_id_) { script_ = *iter; break; } } } UserScript::InjectionType UserScriptInjector::script_type() const { return UserScript::CONTENT_SCRIPT; } bool UserScriptInjector::ShouldExecuteInChildFrames() const { return false; } bool UserScriptInjector::ShouldExecuteInMainWorld() const { return false; } bool UserScriptInjector::IsUserGesture() const { return false; } bool UserScriptInjector::ExpectsResults() const { return false; } bool UserScriptInjector::ShouldInjectJs( UserScript::RunLocation run_location) const { return script_->run_location() == run_location && !script_->js_scripts().empty(); } bool UserScriptInjector::ShouldInjectCss( UserScript::RunLocation run_location) const { return run_location == UserScript::DOCUMENT_START && !script_->css_scripts().empty(); } PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame( const Extension* extension, blink::WebFrame* web_frame, int tab_id, const GURL& top_url) const { // If we don't have a tab id, we have no UI surface to ask for user consent. // For now, we treat this as an automatic allow. if (tab_id == -1) return PermissionsData::ACCESS_ALLOWED; GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL( web_frame, web_frame->document().url(), script_->match_about_blank()); // Declarative user scripts use "page access" (from "permissions" section in // manifest) whereas non-declarative user scripts use custom // "content script access" logic. if (is_declarative_) { return extension->permissions_data()->GetPageAccess( extension, effective_document_url, top_url, tab_id, -1, // no process id NULL /* ignore error */); } else { return extension->permissions_data()->GetContentScriptAccess( extension, effective_document_url, top_url, tab_id, -1, // no process id NULL /* ignore error */); } } std::vector UserScriptInjector::GetJsSources( UserScript::RunLocation run_location) const { DCHECK_EQ(script_->run_location(), run_location); std::vector sources; const UserScript::FileList& js_scripts = script_->js_scripts(); bool is_standalone_or_emulate_greasemonkey = script_->is_standalone() || script_->emulate_greasemonkey(); for (UserScript::FileList::const_iterator iter = js_scripts.begin(); iter != js_scripts.end(); ++iter) { std::string content = iter->GetContent().as_string(); // We add this dumb function wrapper for standalone user script to // emulate what Greasemonkey does. // TODO(aa): I think that maybe "is_standalone" scripts don't exist // anymore. Investigate. if (is_standalone_or_emulate_greasemonkey) { content.insert(0, kUserScriptHead); content += kUserScriptTail; } sources.push_back(blink::WebScriptSource( blink::WebString::fromUTF8(content), iter->url())); } // Emulate Greasemonkey API for scripts that were converted to extensions // and "standalone" user scripts. if (is_standalone_or_emulate_greasemonkey) sources.insert(sources.begin(), g_greasemonkey_api.Get().GetSource()); return sources; } std::vector UserScriptInjector::GetCssSources( UserScript::RunLocation run_location) const { DCHECK_EQ(UserScript::DOCUMENT_START, run_location); std::vector sources; const UserScript::FileList& css_scripts = script_->css_scripts(); for (UserScript::FileList::const_iterator iter = css_scripts.begin(); iter != css_scripts.end(); ++iter) { sources.push_back(iter->GetContent().as_string()); } return sources; } void UserScriptInjector::OnInjectionComplete( scoped_ptr execution_results, ScriptsRunInfo* scripts_run_info, UserScript::RunLocation run_location) { if (ShouldInjectJs(run_location)) { const UserScript::FileList& js_scripts = script_->js_scripts(); scripts_run_info->num_js += js_scripts.size(); for (UserScript::FileList::const_iterator iter = js_scripts.begin(); iter != js_scripts.end(); ++iter) { scripts_run_info->executing_scripts[extension_id_].insert( iter->url().path()); } } if (ShouldInjectCss(run_location)) scripts_run_info->num_css += script_->css_scripts().size(); } void UserScriptInjector::OnWillNotInject(InjectFailureReason reason) { } } // namespace extensions