blob: 5800edf024370586840b842deddb856933b65c09 [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
hanxi05a025052015-04-16 19:15:3247// A map records whether a given |script_id| from a webview-added user script
48// is allowed to inject on the render of given |routing_id|.
49// Once a script is added, the decision of whether or not allowed to inject
50// won't be changed.
51// After removed by the webview, the user scipt will also be removed
52// from the render. Therefore, there won't be any query from the same
53// |script_id| and |routing_id| pair.
54base::LazyInstance<RoutingInfoMap> g_routing_info_map =
55 LAZY_INSTANCE_INITIALIZER;
56
[email protected]ac2f89372014-06-23 21:44:2557// Greasemonkey API source that is injected with the scripts.
58struct GreasemonkeyApiJsString {
59 GreasemonkeyApiJsString();
[email protected]503ae7cd2014-06-25 05:59:3160 blink::WebScriptSource GetSource() const;
61
62 private:
lazyboyde26faa2016-08-22 18:36:2863 blink::WebString source_;
[email protected]ac2f89372014-06-23 21:44:2564};
65
66// The below constructor, monstrous as it is, just makes a WebScriptSource from
67// the GreasemonkeyApiJs resource.
lazyboyde26faa2016-08-22 18:36:2868GreasemonkeyApiJsString::GreasemonkeyApiJsString() {
69 base::StringPiece source_piece =
70 ResourceBundle::GetSharedInstance().GetRawDataResource(
71 IDR_GREASEMONKEY_API_JS);
72 source_ =
73 blink::WebString::fromUTF8(source_piece.data(), source_piece.length());
[email protected]503ae7cd2014-06-25 05:59:3174}
75
76blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const {
lazyboyde26faa2016-08-22 18:36:2877 return blink::WebScriptSource(source_);
[email protected]ac2f89372014-06-23 21:44:2578}
79
80base::LazyInstance<GreasemonkeyApiJsString> g_greasemonkey_api =
81 LAZY_INSTANCE_INITIALIZER;
82
83} // namespace
84
hanxi3df97b22015-03-11 23:40:0685UserScriptInjector::UserScriptInjector(const UserScript* script,
86 UserScriptSet* script_list,
87 bool is_declarative)
[email protected]c11e6592014-06-27 17:07:3488 : script_(script),
lazyboy0f702e92016-09-07 19:03:4589 user_script_set_(script_list),
[email protected]ac2f89372014-06-23 21:44:2590 script_id_(script_->id()),
hanxi3df97b22015-03-11 23:40:0691 host_id_(script_->host_id()),
markdittmer9ea140f2014-08-29 02:46:1592 is_declarative_(is_declarative),
[email protected]ac2f89372014-06-23 21:44:2593 user_script_set_observer_(this) {
94 user_script_set_observer_.Add(script_list);
95}
96
[email protected]c11e6592014-06-27 17:07:3497UserScriptInjector::~UserScriptInjector() {
[email protected]ac2f89372014-06-23 21:44:2598}
99
[email protected]c11e6592014-06-27 17:07:34100void UserScriptInjector::OnUserScriptsUpdated(
hanxi3df97b22015-03-11 23:40:06101 const std::set<HostID>& changed_hosts,
lazyboy12c77d72016-08-19 20:06:09102 const UserScriptList& scripts) {
rdevlin.cronindd7a63a2016-08-30 06:07:17103 // When user scripts are updated, all the old script pointers are invalidated.
104 script_ = nullptr;
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.
rdevlin.cronindd7a63a2016-08-30 06:07:17107 if (changed_hosts.count(host_id_) > 0)
[email protected]ac2f89372014-06-23 21:44:25108 return;
[email protected]ac2f89372014-06-23 21:44:25109
lazyboy8c0f2702016-07-29 16:34:13110 for (const std::unique_ptr<UserScript>& script : scripts) {
lazyboy8c0f2702016-07-29 16:34:13111 if (script->id() == script_id_) {
112 script_ = script.get();
[email protected]ac2f89372014-06-23 21:44:25113 break;
114 }
115 }
rdevlin.cronindd7a63a2016-08-30 06:07:17116 // If |host_id_| wasn't in |changed_hosts|, then the script for this injection
117 // should be guaranteed to exist.
118 DCHECK(script_);
[email protected]ac2f89372014-06-23 21:44:25119}
120
[email protected]23a85362014-07-07 23:26:19121UserScript::InjectionType UserScriptInjector::script_type() const {
122 return UserScript::CONTENT_SCRIPT;
123}
124
[email protected]c11e6592014-06-27 17:07:34125bool UserScriptInjector::ShouldExecuteInMainWorld() const {
126 return false;
127}
128
129bool UserScriptInjector::IsUserGesture() const {
130 return false;
131}
132
133bool UserScriptInjector::ExpectsResults() const {
134 return false;
135}
136
137bool UserScriptInjector::ShouldInjectJs(
138 UserScript::RunLocation run_location) const {
roba8077632016-07-04 23:45:35139 return script_ && script_->run_location() == run_location &&
[email protected]c11e6592014-06-27 17:07:34140 !script_->js_scripts().empty();
141}
142
143bool UserScriptInjector::ShouldInjectCss(
144 UserScript::RunLocation run_location) const {
roba8077632016-07-04 23:45:35145 return script_ && run_location == UserScript::DOCUMENT_START &&
[email protected]c11e6592014-06-27 17:07:34146 !script_->css_scripts().empty();
147}
148
[email protected]23a85362014-07-07 23:26:19149PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame(
hanxia5c856cf2015-02-13 20:51:58150 const InjectionHost* injection_host,
rdevlin.cronin3e11c9862015-06-04 19:54:25151 blink::WebLocalFrame* web_frame,
rdevlin.croninf994d1e2015-06-03 22:28:19152 int tab_id) const {
roba8077632016-07-04 23:45:35153 // There is no harm in allowing the injection when the script is gone,
154 // because there is nothing to inject.
155 if (!script_)
156 return PermissionsData::ACCESS_ALLOWED;
157
rdevlin.croninc318b93d2015-09-14 20:22:29158 if (script_->consumer_instance_type() ==
159 UserScript::ConsumerInstanceType::WEBVIEW) {
160 int routing_id = content::RenderView::FromWebView(web_frame->top()->view())
161 ->GetRoutingID();
162
163 RoutingInfoKey key(routing_id, script_->id());
164
165 RoutingInfoMap& map = g_routing_info_map.Get();
166 auto iter = map.find(key);
167
168 bool allowed = false;
169 if (iter != map.end()) {
170 allowed = iter->second;
171 } else {
172 // Send a SYNC IPC message to the browser to check if this is allowed.
173 // This is not ideal, but is mitigated by the fact that this is only done
174 // for webviews, and then only once per host.
175 // TODO(hanxi): Find a more efficient way to do this.
176 content::RenderThread::Get()->Send(
177 new ExtensionsGuestViewHostMsg_CanExecuteContentScriptSync(
178 routing_id, script_->id(), &allowed));
179 map.insert(std::pair<RoutingInfoKey, bool>(key, allowed));
180 }
181
182 return allowed ? PermissionsData::ACCESS_ALLOWED
183 : PermissionsData::ACCESS_DENIED;
184 }
185
[email protected]f8abc6e42014-06-24 21:14:43186 GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL(
[email protected]c11e6592014-06-27 17:07:34187 web_frame, web_frame->document().url(), script_->match_about_blank());
rdevlin.croninc318b93d2015-09-14 20:22:29188
189 return injection_host->CanExecuteOnFrame(
rdevlin.croninf994d1e2015-06-03 22:28:19190 effective_document_url,
191 content::RenderFrame::FromWebFrame(web_frame),
192 tab_id,
193 is_declarative_);
[email protected]ac2f89372014-06-23 21:44:25194}
195
[email protected]c11e6592014-06-27 17:07:34196std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
rdevlin.croninf05678d2016-08-02 01:10:46197 UserScript::RunLocation run_location) const {
roba8077632016-07-04 23:45:35198 std::vector<blink::WebScriptSource> sources;
199 if (!script_)
200 return sources;
201
[email protected]c11e6592014-06-27 17:07:34202 DCHECK_EQ(script_->run_location(), run_location);
[email protected]ac2f89372014-06-23 21:44:25203
[email protected]c11e6592014-06-27 17:07:34204 const UserScript::FileList& js_scripts = script_->js_scripts();
lazyboy0f702e92016-09-07 19:03:45205 sources.reserve(js_scripts.size() +
206 (script_->emulate_greasemonkey() ? 1 : 0));
[email protected]ac2f89372014-06-23 21:44:25207
rdevlin.cronin12564132016-04-19 00:30:07208 // Emulate Greasemonkey API for scripts that were converted to extension
209 // user scripts.
210 if (script_->emulate_greasemonkey())
lazyboy0f702e92016-09-07 19:03:45211 sources.push_back(g_greasemonkey_api.Get().GetSource());
212
213 for (const std::unique_ptr<UserScript::File>& file : js_scripts) {
214 sources.push_back(blink::WebScriptSource(
215 user_script_set_->GetJsSource(*file, script_->emulate_greasemonkey()),
216 file->url()));
217 }
[email protected]ac2f89372014-06-23 21:44:25218
[email protected]c11e6592014-06-27 17:07:34219 return sources;
[email protected]ac2f89372014-06-23 21:44:25220}
221
lazyboy49cc0b3a2016-08-18 21:55:12222std::vector<blink::WebString> UserScriptInjector::GetCssSources(
rdevlin.croninf05678d2016-08-02 01:10:46223 UserScript::RunLocation run_location) const {
[email protected]c11e6592014-06-27 17:07:34224 DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
225
lazyboy49cc0b3a2016-08-18 21:55:12226 std::vector<blink::WebString> sources;
roba8077632016-07-04 23:45:35227 if (!script_)
228 return sources;
229
[email protected]ac2f89372014-06-23 21:44:25230 const UserScript::FileList& css_scripts = script_->css_scripts();
lazyboy49cc0b3a2016-08-18 21:55:12231 sources.reserve(css_scripts.size());
lazyboy0f702e92016-09-07 19:03:45232 for (const std::unique_ptr<UserScript::File>& file : script_->css_scripts())
233 sources.push_back(user_script_set_->GetCssSource(*file));
[email protected]c11e6592014-06-27 17:07:34234 return sources;
235}
236
kozyatinskiyc8bc9a582015-03-06 09:33:41237void UserScriptInjector::GetRunInfo(
[email protected]c11e6592014-06-27 17:07:34238 ScriptsRunInfo* scripts_run_info,
kozyatinskiyc8bc9a582015-03-06 09:33:41239 UserScript::RunLocation run_location) const {
roba8077632016-07-04 23:45:35240 if (!script_)
241 return;
242
[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();
lazyboy12c77d72016-08-19 20:06:09246 for (const std::unique_ptr<UserScript::File>& iter : js_scripts) {
hanxi3df97b22015-03-11 23:40:06247 scripts_run_info->executing_scripts[host_id_.id()].insert(
[email protected]c11e6592014-06-27 17:07:34248 iter->url().path());
249 }
250 }
251
252 if (ShouldInjectCss(run_location))
253 scripts_run_info->num_css += script_->css_scripts().size();
254}
255
kozyatinskiyc8bc9a582015-03-06 09:33:41256void UserScriptInjector::OnInjectionComplete(
dchengf6f80662016-04-20 20:26:04257 std::unique_ptr<base::Value> execution_result,
rdevlin.cronind533be962015-10-02 17:01:18258 UserScript::RunLocation run_location,
dchengf6f80662016-04-20 20:26:04259 content::RenderFrame* render_frame) {}
kozyatinskiyc8bc9a582015-03-06 09:33:41260
rdevlin.cronind533be962015-10-02 17:01:18261void UserScriptInjector::OnWillNotInject(InjectFailureReason reason,
262 content::RenderFrame* render_frame) {
[email protected]ac2f89372014-06-23 21:44:25263}
264
265} // namespace extensions