blob: 5991210da7b017bf07af58c5fbcca4658084e379 [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"
[email protected]ac2f89372014-06-23 21:44:2522#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.
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 =
70 ResourceBundle::GetSharedInstance().GetRawDataResource(
71 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::ShouldExecuteInMainWorld() const {
137 return false;
138}
139
140bool UserScriptInjector::IsUserGesture() const {
141 return false;
142}
143
144bool UserScriptInjector::ExpectsResults() const {
145 return false;
146}
147
148bool UserScriptInjector::ShouldInjectJs(
catmullingsd4faad4f2016-09-08 19:55:30149 UserScript::RunLocation run_location,
150 const std::set<std::string>& executing_scripts) const {
roba8077632016-07-04 23:45:35151 return script_ && script_->run_location() == run_location &&
catmullingsd4faad4f2016-09-08 19:55:30152 !script_->js_scripts().empty() &&
153 ShouldInjectScripts(script_->js_scripts(), executing_scripts);
[email protected]c11e6592014-06-27 17:07:34154}
155
156bool UserScriptInjector::ShouldInjectCss(
catmullingsd4faad4f2016-09-08 19:55:30157 UserScript::RunLocation run_location,
158 const std::set<std::string>& injected_stylesheets) const {
roba8077632016-07-04 23:45:35159 return script_ && run_location == UserScript::DOCUMENT_START &&
catmullingsd4faad4f2016-09-08 19:55:30160 !script_->css_scripts().empty() &&
161 ShouldInjectScripts(script_->css_scripts(), injected_stylesheets);
[email protected]c11e6592014-06-27 17:07:34162}
163
[email protected]23a85362014-07-07 23:26:19164PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame(
hanxia5c856cf2015-02-13 20:51:58165 const InjectionHost* injection_host,
rdevlin.cronin3e11c9862015-06-04 19:54:25166 blink::WebLocalFrame* web_frame,
jam69e71152016-11-02 01:15:43167 int tab_id) {
roba8077632016-07-04 23:45:35168 // There is no harm in allowing the injection when the script is gone,
169 // because there is nothing to inject.
170 if (!script_)
171 return PermissionsData::ACCESS_ALLOWED;
172
rdevlin.croninc318b93d2015-09-14 20:22:29173 if (script_->consumer_instance_type() ==
174 UserScript::ConsumerInstanceType::WEBVIEW) {
Blink Reformat1c4d759e2017-04-09 16:34:54175 int routing_id = content::RenderView::FromWebView(web_frame->Top()->View())
176 ->GetRoutingID();
rdevlin.croninc318b93d2015-09-14 20:22:29177
178 RoutingInfoKey key(routing_id, script_->id());
179
180 RoutingInfoMap& map = g_routing_info_map.Get();
181 auto iter = map.find(key);
182
183 bool allowed = false;
184 if (iter != map.end()) {
185 allowed = iter->second;
186 } else {
187 // Send a SYNC IPC message to the browser to check if this is allowed.
188 // This is not ideal, but is mitigated by the fact that this is only done
189 // for webviews, and then only once per host.
190 // TODO(hanxi): Find a more efficient way to do this.
191 content::RenderThread::Get()->Send(
192 new ExtensionsGuestViewHostMsg_CanExecuteContentScriptSync(
193 routing_id, script_->id(), &allowed));
194 map.insert(std::pair<RoutingInfoKey, bool>(key, allowed));
195 }
196
197 return allowed ? PermissionsData::ACCESS_ALLOWED
198 : PermissionsData::ACCESS_DENIED;
199 }
200
[email protected]f8abc6e42014-06-24 21:14:43201 GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL(
Blink Reformat1c4d759e2017-04-09 16:34:54202 web_frame, web_frame->GetDocument().Url(), script_->match_about_blank());
rdevlin.croninc318b93d2015-09-14 20:22:29203
204 return injection_host->CanExecuteOnFrame(
rdevlin.croninf994d1e2015-06-03 22:28:19205 effective_document_url,
206 content::RenderFrame::FromWebFrame(web_frame),
207 tab_id,
208 is_declarative_);
[email protected]ac2f89372014-06-23 21:44:25209}
210
[email protected]c11e6592014-06-27 17:07:34211std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
catmullingsd4faad4f2016-09-08 19:55:30212 UserScript::RunLocation run_location,
213 std::set<std::string>* executing_scripts,
214 size_t* num_injected_js_scripts) const {
215 DCHECK(script_);
roba8077632016-07-04 23:45:35216 std::vector<blink::WebScriptSource> sources;
roba8077632016-07-04 23:45:35217
[email protected]c11e6592014-06-27 17:07:34218 DCHECK_EQ(script_->run_location(), run_location);
[email protected]ac2f89372014-06-23 21:44:25219
[email protected]c11e6592014-06-27 17:07:34220 const UserScript::FileList& js_scripts = script_->js_scripts();
lazyboy0f702e92016-09-07 19:03:45221 sources.reserve(js_scripts.size() +
222 (script_->emulate_greasemonkey() ? 1 : 0));
rdevlin.cronin12564132016-04-19 00:30:07223 // Emulate Greasemonkey API for scripts that were converted to extension
224 // user scripts.
225 if (script_->emulate_greasemonkey())
lazyboy0f702e92016-09-07 19:03:45226 sources.push_back(g_greasemonkey_api.Get().GetSource());
lazyboy0f702e92016-09-07 19:03:45227 for (const std::unique_ptr<UserScript::File>& file : js_scripts) {
catmullingsd4faad4f2016-09-08 19:55:30228 const GURL& script_url = file->url();
229 // Check if the script is already injected.
230 if (executing_scripts->count(script_url.path()) != 0)
231 continue;
232
lazyboy0f702e92016-09-07 19:03:45233 sources.push_back(blink::WebScriptSource(
234 user_script_set_->GetJsSource(*file, script_->emulate_greasemonkey()),
catmullingsd4faad4f2016-09-08 19:55:30235 script_url));
236
237 (*num_injected_js_scripts) += 1;
238 executing_scripts->insert(script_url.path());
lazyboy0f702e92016-09-07 19:03:45239 }
[email protected]ac2f89372014-06-23 21:44:25240
[email protected]c11e6592014-06-27 17:07:34241 return sources;
[email protected]ac2f89372014-06-23 21:44:25242}
243
lazyboy49cc0b3a2016-08-18 21:55:12244std::vector<blink::WebString> UserScriptInjector::GetCssSources(
catmullingsd4faad4f2016-09-08 19:55:30245 UserScript::RunLocation run_location,
246 std::set<std::string>* injected_stylesheets,
247 size_t* num_injected_stylesheets) const {
248 DCHECK(script_);
[email protected]c11e6592014-06-27 17:07:34249 DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
250
lazyboy49cc0b3a2016-08-18 21:55:12251 std::vector<blink::WebString> sources;
roba8077632016-07-04 23:45:35252
[email protected]ac2f89372014-06-23 21:44:25253 const UserScript::FileList& css_scripts = script_->css_scripts();
lazyboy49cc0b3a2016-08-18 21:55:12254 sources.reserve(css_scripts.size());
catmullingsd4faad4f2016-09-08 19:55:30255 for (const std::unique_ptr<UserScript::File>& file : script_->css_scripts()) {
256 const std::string& stylesheet_path = file->url().path();
257 // Check if the stylesheet is already injected.
258 if (injected_stylesheets->count(stylesheet_path) != 0)
259 continue;
260
lazyboy0f702e92016-09-07 19:03:45261 sources.push_back(user_script_set_->GetCssSource(*file));
catmullingsd4faad4f2016-09-08 19:55:30262 (*num_injected_stylesheets) += 1;
263 injected_stylesheets->insert(stylesheet_path);
[email protected]c11e6592014-06-27 17:07:34264 }
catmullingsd4faad4f2016-09-08 19:55:30265 return sources;
[email protected]c11e6592014-06-27 17:07:34266}
267
kozyatinskiyc8bc9a582015-03-06 09:33:41268void UserScriptInjector::OnInjectionComplete(
dchengf6f80662016-04-20 20:26:04269 std::unique_ptr<base::Value> execution_result,
rdevlin.cronind533be962015-10-02 17:01:18270 UserScript::RunLocation run_location,
dchengf6f80662016-04-20 20:26:04271 content::RenderFrame* render_frame) {}
kozyatinskiyc8bc9a582015-03-06 09:33:41272
rdevlin.cronind533be962015-10-02 17:01:18273void UserScriptInjector::OnWillNotInject(InjectFailureReason reason,
274 content::RenderFrame* render_frame) {
[email protected]ac2f89372014-06-23 21:44:25275}
276
277} // namespace extensions