blob: 9885b4ea8d38c8d798dd55f301ad24fa16dedf72 [file] [log] [blame]
[email protected]ac2f89372014-06-23 21:44:251// 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
[email protected]c11e6592014-06-27 17:07:345#include "extensions/renderer/user_script_injector.h"
[email protected]ac2f89372014-06-23 21:44:256
jsbell9a9ef2b82015-11-20 19:37:147#include <tuple>
[email protected]ac2f89372014-06-23 21:44:258#include <vector>
9
10#include "base/lazy_instance.h"
[email protected]ac2f89372014-06-23 21:44:2511#include "content/public/common/url_constants.h"
hanxi05a025052015-04-16 19:15:3212#include "content/public/renderer/render_thread.h"
rdevlin.croninf994d1e2015-06-03 22:28:1913#include "content/public/renderer/render_frame.h"
hanxiccff496a2015-03-03 23:14:0614#include "content/public/renderer/render_view.h"
[email protected]ac2f89372014-06-23 21:44:2515#include "extensions/common/extension.h"
fsamuelb0dc17d2015-04-21 18:41:3916#include "extensions/common/guest_view/extensions_guest_view_messages.h"
[email protected]ac2f89372014-06-23 21:44:2517#include "extensions/common/permissions/permissions_data.h"
hanxia5c856cf2015-02-13 20:51:5818#include "extensions/renderer/injection_host.h"
[email protected]f8abc6e42014-06-24 21:14:4319#include "extensions/renderer/script_context.h"
[email protected]c11e6592014-06-27 17:07:3420#include "extensions/renderer/scripts_run_info.h"
[email protected]ac2f89372014-06-23 21:44:2521#include "grit/extensions_renderer_resources.h"
22#include "third_party/WebKit/public/web/WebDocument.h"
rdevlin.cronin3e11c9862015-06-04 19:54:2523#include "third_party/WebKit/public/web/WebLocalFrame.h"
[email protected]ac2f89372014-06-23 21:44:2524#include "third_party/WebKit/public/web/WebScriptSource.h"
[email protected]ac2f89372014-06-23 21:44:2525#include "ui/base/resource/resource_bundle.h"
26#include "url/gurl.h"
27
28namespace extensions {
29
30namespace {
31
hanxi05a025052015-04-16 19:15:3232struct RoutingInfoKey {
33 int routing_id;
34 int script_id;
35
36 RoutingInfoKey(int routing_id, int script_id)
37 : routing_id(routing_id), script_id(script_id) {}
38
39 bool operator<(const RoutingInfoKey& other) const {
jsbell9a9ef2b82015-11-20 19:37:1440 return std::tie(routing_id, script_id) <
41 std::tie(other.routing_id, other.script_id);
hanxi05a025052015-04-16 19:15:3242 }
43};
44
45using RoutingInfoMap = std::map<RoutingInfoKey, bool>;
46
[email protected]ac2f89372014-06-23 21:44:2547// These two strings are injected before and after the Greasemonkey API and
48// user script to wrap it in an anonymous scope.
49const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
50const char kUserScriptTail[] = "\n})(window);";
51
hanxi05a025052015-04-16 19:15:3252// A map records whether a given |script_id| from a webview-added user script
53// is allowed to inject on the render of given |routing_id|.
54// Once a script is added, the decision of whether or not allowed to inject
55// won't be changed.
56// After removed by the webview, the user scipt will also be removed
57// from the render. Therefore, there won't be any query from the same
58// |script_id| and |routing_id| pair.
59base::LazyInstance<RoutingInfoMap> g_routing_info_map =
60 LAZY_INSTANCE_INITIALIZER;
61
[email protected]ac2f89372014-06-23 21:44:2562// Greasemonkey API source that is injected with the scripts.
63struct GreasemonkeyApiJsString {
64 GreasemonkeyApiJsString();
[email protected]503ae7cd2014-06-25 05:59:3165 blink::WebScriptSource GetSource() const;
66
67 private:
68 std::string source_;
[email protected]ac2f89372014-06-23 21:44:2569};
70
71// The below constructor, monstrous as it is, just makes a WebScriptSource from
72// the GreasemonkeyApiJs resource.
73GreasemonkeyApiJsString::GreasemonkeyApiJsString()
[email protected]503ae7cd2014-06-25 05:59:3174 : source_(ResourceBundle::GetSharedInstance()
75 .GetRawDataResource(IDR_GREASEMONKEY_API_JS)
76 .as_string()) {
77}
78
79blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const {
80 return blink::WebScriptSource(blink::WebString::fromUTF8(source_));
[email protected]ac2f89372014-06-23 21:44:2581}
82
83base::LazyInstance<GreasemonkeyApiJsString> g_greasemonkey_api =
84 LAZY_INSTANCE_INITIALIZER;
85
86} // namespace
87
hanxi3df97b22015-03-11 23:40:0688UserScriptInjector::UserScriptInjector(const UserScript* script,
89 UserScriptSet* script_list,
90 bool is_declarative)
[email protected]c11e6592014-06-27 17:07:3491 : script_(script),
[email protected]ac2f89372014-06-23 21:44:2592 script_id_(script_->id()),
hanxi3df97b22015-03-11 23:40:0693 host_id_(script_->host_id()),
markdittmer9ea140f2014-08-29 02:46:1594 is_declarative_(is_declarative),
[email protected]ac2f89372014-06-23 21:44:2595 user_script_set_observer_(this) {
96 user_script_set_observer_.Add(script_list);
97}
98
[email protected]c11e6592014-06-27 17:07:3499UserScriptInjector::~UserScriptInjector() {
[email protected]ac2f89372014-06-23 21:44:25100}
101
[email protected]c11e6592014-06-27 17:07:34102void UserScriptInjector::OnUserScriptsUpdated(
hanxi3df97b22015-03-11 23:40:06103 const std::set<HostID>& changed_hosts,
[email protected]ac2f89372014-06-23 21:44:25104 const std::vector<UserScript*>& scripts) {
hanxi3df97b22015-03-11 23:40:06105 // If the host causing this injection changed, then this injection
[email protected]ac2f89372014-06-23 21:44:25106 // will be removed, and there's no guarantee the backing script still exists.
hanxi3df97b22015-03-11 23:40:06107 if (changed_hosts.count(host_id_) > 0)
[email protected]ac2f89372014-06-23 21:44:25108 return;
109
110 for (std::vector<UserScript*>::const_iterator iter = scripts.begin();
111 iter != scripts.end();
112 ++iter) {
113 // We need to compare to |script_id_| (and not to script_->id()) because the
114 // old |script_| may be deleted by now.
115 if ((*iter)->id() == script_id_) {
116 script_ = *iter;
117 break;
118 }
119 }
120}
121
[email protected]23a85362014-07-07 23:26:19122UserScript::InjectionType UserScriptInjector::script_type() const {
123 return UserScript::CONTENT_SCRIPT;
124}
125
[email protected]c11e6592014-06-27 17:07:34126bool UserScriptInjector::ShouldExecuteInMainWorld() const {
127 return false;
128}
129
130bool UserScriptInjector::IsUserGesture() const {
131 return false;
132}
133
134bool UserScriptInjector::ExpectsResults() const {
135 return false;
136}
137
138bool UserScriptInjector::ShouldInjectJs(
139 UserScript::RunLocation run_location) const {
140 return script_->run_location() == run_location &&
141 !script_->js_scripts().empty();
142}
143
144bool UserScriptInjector::ShouldInjectCss(
145 UserScript::RunLocation run_location) const {
146 return run_location == UserScript::DOCUMENT_START &&
147 !script_->css_scripts().empty();
148}
149
[email protected]23a85362014-07-07 23:26:19150PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame(
hanxia5c856cf2015-02-13 20:51:58151 const InjectionHost* injection_host,
rdevlin.cronin3e11c9862015-06-04 19:54:25152 blink::WebLocalFrame* web_frame,
rdevlin.croninf994d1e2015-06-03 22:28:19153 int tab_id) const {
rdevlin.croninc318b93d2015-09-14 20:22:29154 if (script_->consumer_instance_type() ==
155 UserScript::ConsumerInstanceType::WEBVIEW) {
156 int routing_id = content::RenderView::FromWebView(web_frame->top()->view())
157 ->GetRoutingID();
158
159 RoutingInfoKey key(routing_id, script_->id());
160
161 RoutingInfoMap& map = g_routing_info_map.Get();
162 auto iter = map.find(key);
163
164 bool allowed = false;
165 if (iter != map.end()) {
166 allowed = iter->second;
167 } else {
168 // Send a SYNC IPC message to the browser to check if this is allowed.
169 // This is not ideal, but is mitigated by the fact that this is only done
170 // for webviews, and then only once per host.
171 // TODO(hanxi): Find a more efficient way to do this.
172 content::RenderThread::Get()->Send(
173 new ExtensionsGuestViewHostMsg_CanExecuteContentScriptSync(
174 routing_id, script_->id(), &allowed));
175 map.insert(std::pair<RoutingInfoKey, bool>(key, allowed));
176 }
177
178 return allowed ? PermissionsData::ACCESS_ALLOWED
179 : PermissionsData::ACCESS_DENIED;
180 }
181
[email protected]f8abc6e42014-06-24 21:14:43182 GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL(
[email protected]c11e6592014-06-27 17:07:34183 web_frame, web_frame->document().url(), script_->match_about_blank());
rdevlin.croninc318b93d2015-09-14 20:22:29184
185 return injection_host->CanExecuteOnFrame(
rdevlin.croninf994d1e2015-06-03 22:28:19186 effective_document_url,
187 content::RenderFrame::FromWebFrame(web_frame),
188 tab_id,
189 is_declarative_);
[email protected]ac2f89372014-06-23 21:44:25190}
191
[email protected]c11e6592014-06-27 17:07:34192std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
193 UserScript::RunLocation run_location) const {
194 DCHECK_EQ(script_->run_location(), run_location);
[email protected]ac2f89372014-06-23 21:44:25195
[email protected]ac2f89372014-06-23 21:44:25196 std::vector<blink::WebScriptSource> sources;
[email protected]c11e6592014-06-27 17:07:34197 const UserScript::FileList& js_scripts = script_->js_scripts();
[email protected]ac2f89372014-06-23 21:44:25198 bool is_standalone_or_emulate_greasemonkey =
199 script_->is_standalone() || script_->emulate_greasemonkey();
[email protected]c11e6592014-06-27 17:07:34200
[email protected]ac2f89372014-06-23 21:44:25201 for (UserScript::FileList::const_iterator iter = js_scripts.begin();
202 iter != js_scripts.end();
203 ++iter) {
204 std::string content = iter->GetContent().as_string();
205
206 // We add this dumb function wrapper for standalone user script to
207 // emulate what Greasemonkey does.
208 // TODO(aa): I think that maybe "is_standalone" scripts don't exist
209 // anymore. Investigate.
210 if (is_standalone_or_emulate_greasemonkey) {
211 content.insert(0, kUserScriptHead);
212 content += kUserScriptTail;
213 }
214 sources.push_back(blink::WebScriptSource(
215 blink::WebString::fromUTF8(content), iter->url()));
216 }
217
218 // Emulate Greasemonkey API for scripts that were converted to extensions
219 // and "standalone" user scripts.
220 if (is_standalone_or_emulate_greasemonkey)
[email protected]503ae7cd2014-06-25 05:59:31221 sources.insert(sources.begin(), g_greasemonkey_api.Get().GetSource());
[email protected]ac2f89372014-06-23 21:44:25222
[email protected]c11e6592014-06-27 17:07:34223 return sources;
[email protected]ac2f89372014-06-23 21:44:25224}
225
[email protected]c11e6592014-06-27 17:07:34226std::vector<std::string> UserScriptInjector::GetCssSources(
227 UserScript::RunLocation run_location) const {
228 DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
229
230 std::vector<std::string> sources;
[email protected]ac2f89372014-06-23 21:44:25231 const UserScript::FileList& css_scripts = script_->css_scripts();
[email protected]ac2f89372014-06-23 21:44:25232 for (UserScript::FileList::const_iterator iter = css_scripts.begin();
233 iter != css_scripts.end();
234 ++iter) {
[email protected]c11e6592014-06-27 17:07:34235 sources.push_back(iter->GetContent().as_string());
[email protected]ac2f89372014-06-23 21:44:25236 }
[email protected]c11e6592014-06-27 17:07:34237 return sources;
238}
239
kozyatinskiyc8bc9a582015-03-06 09:33:41240void UserScriptInjector::GetRunInfo(
[email protected]c11e6592014-06-27 17:07:34241 ScriptsRunInfo* scripts_run_info,
kozyatinskiyc8bc9a582015-03-06 09:33:41242 UserScript::RunLocation run_location) const {
[email protected]c11e6592014-06-27 17:07:34243 if (ShouldInjectJs(run_location)) {
244 const UserScript::FileList& js_scripts = script_->js_scripts();
245 scripts_run_info->num_js += js_scripts.size();
246 for (UserScript::FileList::const_iterator iter = js_scripts.begin();
247 iter != js_scripts.end();
248 ++iter) {
hanxi3df97b22015-03-11 23:40:06249 scripts_run_info->executing_scripts[host_id_.id()].insert(
[email protected]c11e6592014-06-27 17:07:34250 iter->url().path());
251 }
252 }
253
254 if (ShouldInjectCss(run_location))
255 scripts_run_info->num_css += script_->css_scripts().size();
256}
257
kozyatinskiyc8bc9a582015-03-06 09:33:41258void UserScriptInjector::OnInjectionComplete(
rdevlin.cronin4bb32d72015-06-02 21:55:01259 scoped_ptr<base::Value> execution_result,
rdevlin.cronind533be962015-10-02 17:01:18260 UserScript::RunLocation run_location,
261 content::RenderFrame* render_frame) {
kozyatinskiyc8bc9a582015-03-06 09:33:41262}
263
rdevlin.cronind533be962015-10-02 17:01:18264void UserScriptInjector::OnWillNotInject(InjectFailureReason reason,
265 content::RenderFrame* render_frame) {
[email protected]ac2f89372014-06-23 21:44:25266}
267
268} // namespace extensions