blob: 3a9adf428950fddc681aad8484da7379bb9efa6e [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"
rdevlin.croninf994d1e2015-06-03 22:28:1912#include "content/public/renderer/render_frame.h"
thakis54f7dc92017-02-23 22:09:5913#include "content/public/renderer/render_thread.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"
thakis54f7dc92017-02-23 22:09:5918#include "extensions/grit/extensions_renderer_resources.h"
hanxia5c856cf2015-02-13 20:51:5819#include "extensions/renderer/injection_host.h"
[email protected]f8abc6e42014-06-24 21:14:4320#include "extensions/renderer/script_context.h"
[email protected]c11e6592014-06-27 17:07:3421#include "extensions/renderer/scripts_run_info.h"
Blink Reformata30d4232018-04-07 15:31:0622#include "third_party/blink/public/web/web_document.h"
23#include "third_party/blink/public/web/web_local_frame.h"
24#include "third_party/blink/public/web/web_script_source.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.
scottmg5e65e3a2017-03-08 08:48:4654base::LazyInstance<RoutingInfoMap>::DestructorAtExit g_routing_info_map =
hanxi05a025052015-04-16 19:15:3255 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 =
Lei Zhangcf30efc2017-10-04 21:31:2470 ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
lazyboyde26faa2016-08-22 18:36:2871 IDR_GREASEMONKEY_API_JS);
72 source_ =
Blink Reformat1c4d759e2017-04-09 16:34:5473 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
lazyboy10961902016-09-10 03:08:3580base::LazyInstance<GreasemonkeyApiJsString>::Leaky g_greasemonkey_api =
[email protected]ac2f89372014-06-23 21:44:2581 LAZY_INSTANCE_INITIALIZER;
82
catmullingsd4faad4f2016-09-08 19:55:3083bool ShouldInjectScripts(const UserScript::FileList& scripts,
84 const std::set<std::string>& injected_files) {
85 for (const std::unique_ptr<UserScript::File>& file : scripts) {
86 // Check if the script is already injected.
87 if (injected_files.count(file->url().path()) == 0) {
88 return true;
89 }
90 }
91 return false;
92}
93
[email protected]ac2f89372014-06-23 21:44:2594} // namespace
95
hanxi3df97b22015-03-11 23:40:0696UserScriptInjector::UserScriptInjector(const UserScript* script,
97 UserScriptSet* script_list,
98 bool is_declarative)
[email protected]c11e6592014-06-27 17:07:3499 : script_(script),
lazyboy0f702e92016-09-07 19:03:45100 user_script_set_(script_list),
[email protected]ac2f89372014-06-23 21:44:25101 script_id_(script_->id()),
hanxi3df97b22015-03-11 23:40:06102 host_id_(script_->host_id()),
markdittmer9ea140f2014-08-29 02:46:15103 is_declarative_(is_declarative),
[email protected]ac2f89372014-06-23 21:44:25104 user_script_set_observer_(this) {
105 user_script_set_observer_.Add(script_list);
106}
107
[email protected]c11e6592014-06-27 17:07:34108UserScriptInjector::~UserScriptInjector() {
[email protected]ac2f89372014-06-23 21:44:25109}
110
[email protected]c11e6592014-06-27 17:07:34111void UserScriptInjector::OnUserScriptsUpdated(
hanxi3df97b22015-03-11 23:40:06112 const std::set<HostID>& changed_hosts,
lazyboy12c77d72016-08-19 20:06:09113 const UserScriptList& scripts) {
rdevlin.cronindd7a63a2016-08-30 06:07:17114 // When user scripts are updated, all the old script pointers are invalidated.
115 script_ = nullptr;
hanxi3df97b22015-03-11 23:40:06116 // If the host causing this injection changed, then this injection
[email protected]ac2f89372014-06-23 21:44:25117 // will be removed, and there's no guarantee the backing script still exists.
rdevlin.cronindd7a63a2016-08-30 06:07:17118 if (changed_hosts.count(host_id_) > 0)
[email protected]ac2f89372014-06-23 21:44:25119 return;
120
lazyboy8c0f2702016-07-29 16:34:13121 for (const std::unique_ptr<UserScript>& script : scripts) {
lazyboy8c0f2702016-07-29 16:34:13122 if (script->id() == script_id_) {
123 script_ = script.get();
[email protected]ac2f89372014-06-23 21:44:25124 break;
125 }
126 }
rdevlin.cronindd7a63a2016-08-30 06:07:17127 // If |host_id_| wasn't in |changed_hosts|, then the script for this injection
128 // should be guaranteed to exist.
129 DCHECK(script_);
[email protected]ac2f89372014-06-23 21:44:25130}
131
[email protected]23a85362014-07-07 23:26:19132UserScript::InjectionType UserScriptInjector::script_type() const {
133 return UserScript::CONTENT_SCRIPT;
134}
135
[email protected]c11e6592014-06-27 17:07:34136bool UserScriptInjector::IsUserGesture() const {
137 return false;
138}
139
140bool UserScriptInjector::ExpectsResults() const {
141 return false;
142}
143
Manish Jethani9494d722018-01-20 00:28:47144base::Optional<CSSOrigin> UserScriptInjector::GetCssOrigin() const {
145 return base::nullopt;
146}
147
Manish Jethaniff6ff852018-02-23 07:24:55148const base::Optional<std::string> UserScriptInjector::GetInjectionKey() const {
149 return base::nullopt;
150}
151
[email protected]c11e6592014-06-27 17:07:34152bool UserScriptInjector::ShouldInjectJs(
catmullingsd4faad4f2016-09-08 19:55:30153 UserScript::RunLocation run_location,
154 const std::set<std::string>& executing_scripts) const {
roba8077632016-07-04 23:45:35155 return script_ && script_->run_location() == run_location &&
catmullingsd4faad4f2016-09-08 19:55:30156 !script_->js_scripts().empty() &&
157 ShouldInjectScripts(script_->js_scripts(), executing_scripts);
[email protected]c11e6592014-06-27 17:07:34158}
159
160bool UserScriptInjector::ShouldInjectCss(
catmullingsd4faad4f2016-09-08 19:55:30161 UserScript::RunLocation run_location,
162 const std::set<std::string>& injected_stylesheets) const {
roba8077632016-07-04 23:45:35163 return script_ && run_location == UserScript::DOCUMENT_START &&
catmullingsd4faad4f2016-09-08 19:55:30164 !script_->css_scripts().empty() &&
165 ShouldInjectScripts(script_->css_scripts(), injected_stylesheets);
[email protected]c11e6592014-06-27 17:07:34166}
167
Devlin Cronin3e532b82018-05-03 21:27:19168PermissionsData::PageAccess UserScriptInjector::CanExecuteOnFrame(
hanxia5c856cf2015-02-13 20:51:58169 const InjectionHost* injection_host,
rdevlin.cronin3e11c9862015-06-04 19:54:25170 blink::WebLocalFrame* web_frame,
jam69e71152016-11-02 01:15:43171 int tab_id) {
roba8077632016-07-04 23:45:35172 // There is no harm in allowing the injection when the script is gone,
173 // because there is nothing to inject.
174 if (!script_)
Devlin Cronin3e532b82018-05-03 21:27:19175 return PermissionsData::PageAccess::kAllowed;
roba8077632016-07-04 23:45:35176
rdevlin.croninc318b93d2015-09-14 20:22:29177 if (script_->consumer_instance_type() ==
178 UserScript::ConsumerInstanceType::WEBVIEW) {
Blink Reformat1c4d759e2017-04-09 16:34:54179 int routing_id = content::RenderView::FromWebView(web_frame->Top()->View())
180 ->GetRoutingID();
rdevlin.croninc318b93d2015-09-14 20:22:29181
182 RoutingInfoKey key(routing_id, script_->id());
183
184 RoutingInfoMap& map = g_routing_info_map.Get();
185 auto iter = map.find(key);
186
187 bool allowed = false;
188 if (iter != map.end()) {
189 allowed = iter->second;
190 } else {
191 // Send a SYNC IPC message to the browser to check if this is allowed.
192 // This is not ideal, but is mitigated by the fact that this is only done
193 // for webviews, and then only once per host.
194 // TODO(hanxi): Find a more efficient way to do this.
195 content::RenderThread::Get()->Send(
196 new ExtensionsGuestViewHostMsg_CanExecuteContentScriptSync(
197 routing_id, script_->id(), &allowed));
198 map.insert(std::pair<RoutingInfoKey, bool>(key, allowed));
199 }
200
Devlin Cronin3e532b82018-05-03 21:27:19201 return allowed ? PermissionsData::PageAccess::kAllowed
202 : PermissionsData::PageAccess::kDenied;
rdevlin.croninc318b93d2015-09-14 20:22:29203 }
204
[email protected]f8abc6e42014-06-24 21:14:43205 GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL(
Blink Reformat1c4d759e2017-04-09 16:34:54206 web_frame, web_frame->GetDocument().Url(), script_->match_about_blank());
rdevlin.croninc318b93d2015-09-14 20:22:29207
208 return injection_host->CanExecuteOnFrame(
rdevlin.croninf994d1e2015-06-03 22:28:19209 effective_document_url,
210 content::RenderFrame::FromWebFrame(web_frame),
211 tab_id,
212 is_declarative_);
[email protected]ac2f89372014-06-23 21:44:25213}
214
[email protected]c11e6592014-06-27 17:07:34215std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
catmullingsd4faad4f2016-09-08 19:55:30216 UserScript::RunLocation run_location,
217 std::set<std::string>* executing_scripts,
218 size_t* num_injected_js_scripts) const {
219 DCHECK(script_);
roba8077632016-07-04 23:45:35220 std::vector<blink::WebScriptSource> sources;
roba8077632016-07-04 23:45:35221
[email protected]c11e6592014-06-27 17:07:34222 DCHECK_EQ(script_->run_location(), run_location);
[email protected]ac2f89372014-06-23 21:44:25223
[email protected]c11e6592014-06-27 17:07:34224 const UserScript::FileList& js_scripts = script_->js_scripts();
lazyboy0f702e92016-09-07 19:03:45225 sources.reserve(js_scripts.size() +
226 (script_->emulate_greasemonkey() ? 1 : 0));
rdevlin.cronin12564132016-04-19 00:30:07227 // Emulate Greasemonkey API for scripts that were converted to extension
228 // user scripts.
229 if (script_->emulate_greasemonkey())
lazyboy0f702e92016-09-07 19:03:45230 sources.push_back(g_greasemonkey_api.Get().GetSource());
lazyboy0f702e92016-09-07 19:03:45231 for (const std::unique_ptr<UserScript::File>& file : js_scripts) {
catmullingsd4faad4f2016-09-08 19:55:30232 const GURL& script_url = file->url();
233 // Check if the script is already injected.
234 if (executing_scripts->count(script_url.path()) != 0)
235 continue;
236
lazyboy0f702e92016-09-07 19:03:45237 sources.push_back(blink::WebScriptSource(
238 user_script_set_->GetJsSource(*file, script_->emulate_greasemonkey()),
catmullingsd4faad4f2016-09-08 19:55:30239 script_url));
240
241 (*num_injected_js_scripts) += 1;
242 executing_scripts->insert(script_url.path());
lazyboy0f702e92016-09-07 19:03:45243 }
[email protected]ac2f89372014-06-23 21:44:25244
[email protected]c11e6592014-06-27 17:07:34245 return sources;
[email protected]ac2f89372014-06-23 21:44:25246}
247
lazyboy49cc0b3a2016-08-18 21:55:12248std::vector<blink::WebString> UserScriptInjector::GetCssSources(
catmullingsd4faad4f2016-09-08 19:55:30249 UserScript::RunLocation run_location,
250 std::set<std::string>* injected_stylesheets,
251 size_t* num_injected_stylesheets) const {
252 DCHECK(script_);
[email protected]c11e6592014-06-27 17:07:34253 DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
254
lazyboy49cc0b3a2016-08-18 21:55:12255 std::vector<blink::WebString> sources;
roba8077632016-07-04 23:45:35256
[email protected]ac2f89372014-06-23 21:44:25257 const UserScript::FileList& css_scripts = script_->css_scripts();
lazyboy49cc0b3a2016-08-18 21:55:12258 sources.reserve(css_scripts.size());
catmullingsd4faad4f2016-09-08 19:55:30259 for (const std::unique_ptr<UserScript::File>& file : script_->css_scripts()) {
260 const std::string& stylesheet_path = file->url().path();
261 // Check if the stylesheet is already injected.
262 if (injected_stylesheets->count(stylesheet_path) != 0)
263 continue;
264
lazyboy0f702e92016-09-07 19:03:45265 sources.push_back(user_script_set_->GetCssSource(*file));
catmullingsd4faad4f2016-09-08 19:55:30266 (*num_injected_stylesheets) += 1;
267 injected_stylesheets->insert(stylesheet_path);
[email protected]c11e6592014-06-27 17:07:34268 }
catmullingsd4faad4f2016-09-08 19:55:30269 return sources;
[email protected]c11e6592014-06-27 17:07:34270}
271
kozyatinskiyc8bc9a582015-03-06 09:33:41272void UserScriptInjector::OnInjectionComplete(
dchengf6f80662016-04-20 20:26:04273 std::unique_ptr<base::Value> execution_result,
rdevlin.cronind533be962015-10-02 17:01:18274 UserScript::RunLocation run_location,
dchengf6f80662016-04-20 20:26:04275 content::RenderFrame* render_frame) {}
kozyatinskiyc8bc9a582015-03-06 09:33:41276
rdevlin.cronind533be962015-10-02 17:01:18277void UserScriptInjector::OnWillNotInject(InjectFailureReason reason,
278 content::RenderFrame* render_frame) {
[email protected]ac2f89372014-06-23 21:44:25279}
280
281} // namespace extensions