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_injector.h" 6 7#include <vector> 8 9#include "base/lazy_instance.h" 10#include "content/public/common/url_constants.h" 11#include "extensions/common/extension.h" 12#include "extensions/common/permissions/permissions_data.h" 13#include "extensions/renderer/script_context.h" 14#include "extensions/renderer/scripts_run_info.h" 15#include "grit/extensions_renderer_resources.h" 16#include "third_party/WebKit/public/web/WebDocument.h" 17#include "third_party/WebKit/public/web/WebFrame.h" 18#include "third_party/WebKit/public/web/WebScriptSource.h" 19#include "ui/base/resource/resource_bundle.h" 20#include "url/gurl.h" 21 22namespace extensions { 23 24namespace { 25 26// These two strings are injected before and after the Greasemonkey API and 27// user script to wrap it in an anonymous scope. 28const char kUserScriptHead[] = "(function (unsafeWindow) {\n"; 29const char kUserScriptTail[] = "\n})(window);"; 30 31// Greasemonkey API source that is injected with the scripts. 32struct GreasemonkeyApiJsString { 33 GreasemonkeyApiJsString(); 34 blink::WebScriptSource GetSource() const; 35 36 private: 37 std::string source_; 38}; 39 40// The below constructor, monstrous as it is, just makes a WebScriptSource from 41// the GreasemonkeyApiJs resource. 42GreasemonkeyApiJsString::GreasemonkeyApiJsString() 43 : source_(ResourceBundle::GetSharedInstance() 44 .GetRawDataResource(IDR_GREASEMONKEY_API_JS) 45 .as_string()) { 46} 47 48blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const { 49 return blink::WebScriptSource(blink::WebString::fromUTF8(source_)); 50} 51 52base::LazyInstance<GreasemonkeyApiJsString> g_greasemonkey_api = 53 LAZY_INSTANCE_INITIALIZER; 54 55} // namespace 56 57UserScriptInjector::UserScriptInjector( 58 const UserScript* script, 59 UserScriptSet* script_list, 60 bool is_declarative) 61 : script_(script), 62 script_id_(script_->id()), 63 extension_id_(script_->extension_id()), 64 is_declarative_(is_declarative), 65 user_script_set_observer_(this) { 66 user_script_set_observer_.Add(script_list); 67} 68 69UserScriptInjector::~UserScriptInjector() { 70} 71 72void UserScriptInjector::OnUserScriptsUpdated( 73 const std::set<std::string>& changed_extensions, 74 const std::vector<UserScript*>& scripts) { 75 // If the extension causing this injection changed, then this injection 76 // will be removed, and there's no guarantee the backing script still exists. 77 if (changed_extensions.count(extension_id_) > 0) 78 return; 79 80 for (std::vector<UserScript*>::const_iterator iter = scripts.begin(); 81 iter != scripts.end(); 82 ++iter) { 83 // We need to compare to |script_id_| (and not to script_->id()) because the 84 // old |script_| may be deleted by now. 85 if ((*iter)->id() == script_id_) { 86 script_ = *iter; 87 break; 88 } 89 } 90} 91 92UserScript::InjectionType UserScriptInjector::script_type() const { 93 return UserScript::CONTENT_SCRIPT; 94} 95 96bool UserScriptInjector::ShouldExecuteInChildFrames() const { 97 return false; 98} 99 100bool UserScriptInjector::ShouldExecuteInMainWorld() const { 101 return false; 102} 103 104bool UserScriptInjector::IsUserGesture() const { 105 return false; 106} 107 108bool UserScriptInjector::ExpectsResults() const { 109 return false; 110} 111 112bool UserScriptInjector::ShouldInjectJs( 113 UserScript::RunLocation run_location) const { 114 return script_->run_location() == run_location && 115 !script_->js_scripts().empty(); 116} 117 118bool UserScriptInjector::ShouldInjectCss( 119 UserScript::RunLocation run_location) const { 120 return run_location == UserScript::DOCUMENT_START && 121 !script_->css_scripts().empty(); 122} 123 124PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame( 125 const Extension* extension, 126 blink::WebFrame* web_frame, 127 int tab_id, 128 const GURL& top_url) const { 129 // If we don't have a tab id, we have no UI surface to ask for user consent. 130 // For now, we treat this as an automatic allow. 131 if (tab_id == -1) 132 return PermissionsData::ACCESS_ALLOWED; 133 134 GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL( 135 web_frame, web_frame->document().url(), script_->match_about_blank()); 136 137 // Declarative user scripts use "page access" (from "permissions" section in 138 // manifest) whereas non-declarative user scripts use custom 139 // "content script access" logic. 140 if (is_declarative_) { 141 return extension->permissions_data()->GetPageAccess( 142 extension, 143 effective_document_url, 144 top_url, 145 tab_id, 146 -1, // no process id 147 NULL /* ignore error */); 148 } else { 149 return extension->permissions_data()->GetContentScriptAccess( 150 extension, 151 effective_document_url, 152 top_url, 153 tab_id, 154 -1, // no process id 155 NULL /* ignore error */); 156 } 157} 158 159std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources( 160 UserScript::RunLocation run_location) const { 161 DCHECK_EQ(script_->run_location(), run_location); 162 163 std::vector<blink::WebScriptSource> sources; 164 const UserScript::FileList& js_scripts = script_->js_scripts(); 165 bool is_standalone_or_emulate_greasemonkey = 166 script_->is_standalone() || script_->emulate_greasemonkey(); 167 168 for (UserScript::FileList::const_iterator iter = js_scripts.begin(); 169 iter != js_scripts.end(); 170 ++iter) { 171 std::string content = iter->GetContent().as_string(); 172 173 // We add this dumb function wrapper for standalone user script to 174 // emulate what Greasemonkey does. 175 // TODO(aa): I think that maybe "is_standalone" scripts don't exist 176 // anymore. Investigate. 177 if (is_standalone_or_emulate_greasemonkey) { 178 content.insert(0, kUserScriptHead); 179 content += kUserScriptTail; 180 } 181 sources.push_back(blink::WebScriptSource( 182 blink::WebString::fromUTF8(content), iter->url())); 183 } 184 185 // Emulate Greasemonkey API for scripts that were converted to extensions 186 // and "standalone" user scripts. 187 if (is_standalone_or_emulate_greasemonkey) 188 sources.insert(sources.begin(), g_greasemonkey_api.Get().GetSource()); 189 190 return sources; 191} 192 193std::vector<std::string> UserScriptInjector::GetCssSources( 194 UserScript::RunLocation run_location) const { 195 DCHECK_EQ(UserScript::DOCUMENT_START, run_location); 196 197 std::vector<std::string> sources; 198 const UserScript::FileList& css_scripts = script_->css_scripts(); 199 for (UserScript::FileList::const_iterator iter = css_scripts.begin(); 200 iter != css_scripts.end(); 201 ++iter) { 202 sources.push_back(iter->GetContent().as_string()); 203 } 204 return sources; 205} 206 207void UserScriptInjector::OnInjectionComplete( 208 scoped_ptr<base::ListValue> execution_results, 209 ScriptsRunInfo* scripts_run_info, 210 UserScript::RunLocation run_location) { 211 if (ShouldInjectJs(run_location)) { 212 const UserScript::FileList& js_scripts = script_->js_scripts(); 213 scripts_run_info->num_js += js_scripts.size(); 214 for (UserScript::FileList::const_iterator iter = js_scripts.begin(); 215 iter != js_scripts.end(); 216 ++iter) { 217 scripts_run_info->executing_scripts[extension_id_].insert( 218 iter->url().path()); 219 } 220 } 221 222 if (ShouldInjectCss(run_location)) 223 scripts_run_info->num_css += script_->css_scripts().size(); 224} 225 226void UserScriptInjector::OnWillNotInject(InjectFailureReason reason) { 227} 228 229} // namespace extensions 230