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.
5 #include "extensions/renderer/user_script_injector.h"
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/injection_host.h"
14 #include "extensions/renderer/script_context.h"
15 #include "extensions/renderer/scripts_run_info.h"
16 #include "grit/extensions_renderer_resources.h"
17 #include "third_party/WebKit/public/web/WebDocument.h"
18 #include "third_party/WebKit/public/web/WebFrame.h"
19 #include "third_party/WebKit/public/web/WebScriptSource.h"
20 #include "ui/base/resource/resource_bundle.h"
23 namespace extensions
{
27 // These two strings are injected before and after the Greasemonkey API and
28 // user script to wrap it in an anonymous scope.
29 const char kUserScriptHead
[] = "(function (unsafeWindow) {\n";
30 const char kUserScriptTail
[] = "\n})(window);";
32 // Greasemonkey API source that is injected with the scripts.
33 struct GreasemonkeyApiJsString
{
34 GreasemonkeyApiJsString();
35 blink::WebScriptSource
GetSource() const;
41 // The below constructor, monstrous as it is, just makes a WebScriptSource from
42 // the GreasemonkeyApiJs resource.
43 GreasemonkeyApiJsString::GreasemonkeyApiJsString()
44 : source_(ResourceBundle::GetSharedInstance()
45 .GetRawDataResource(IDR_GREASEMONKEY_API_JS
)
49 blink::WebScriptSource
GreasemonkeyApiJsString::GetSource() const {
50 return blink::WebScriptSource(blink::WebString::fromUTF8(source_
));
53 base::LazyInstance
<GreasemonkeyApiJsString
> g_greasemonkey_api
=
54 LAZY_INSTANCE_INITIALIZER
;
58 UserScriptInjector::UserScriptInjector(
59 const UserScript
* script
,
60 UserScriptSet
* script_list
,
63 script_id_(script_
->id()),
64 extension_id_(script_
->extension_id()),
65 is_declarative_(is_declarative
),
66 user_script_set_observer_(this) {
67 user_script_set_observer_
.Add(script_list
);
70 UserScriptInjector::~UserScriptInjector() {
73 void UserScriptInjector::OnUserScriptsUpdated(
74 const std::set
<std::string
>& changed_extensions
,
75 const std::vector
<UserScript
*>& scripts
) {
76 // If the extension causing this injection changed, then this injection
77 // will be removed, and there's no guarantee the backing script still exists.
78 if (changed_extensions
.count(extension_id_
) > 0)
81 for (std::vector
<UserScript
*>::const_iterator iter
= scripts
.begin();
82 iter
!= scripts
.end();
84 // We need to compare to |script_id_| (and not to script_->id()) because the
85 // old |script_| may be deleted by now.
86 if ((*iter
)->id() == script_id_
) {
93 UserScript::InjectionType
UserScriptInjector::script_type() const {
94 return UserScript::CONTENT_SCRIPT
;
97 bool UserScriptInjector::ShouldExecuteInChildFrames() const {
101 bool UserScriptInjector::ShouldExecuteInMainWorld() const {
105 bool UserScriptInjector::IsUserGesture() const {
109 bool UserScriptInjector::ExpectsResults() const {
113 bool UserScriptInjector::ShouldInjectJs(
114 UserScript::RunLocation run_location
) const {
115 return script_
->run_location() == run_location
&&
116 !script_
->js_scripts().empty();
119 bool UserScriptInjector::ShouldInjectCss(
120 UserScript::RunLocation run_location
) const {
121 return run_location
== UserScript::DOCUMENT_START
&&
122 !script_
->css_scripts().empty();
125 PermissionsData::AccessType
UserScriptInjector::CanExecuteOnFrame(
126 const InjectionHost
* injection_host
,
127 blink::WebFrame
* web_frame
,
129 const GURL
& top_url
) const {
130 // If we don't have a tab id, we have no UI surface to ask for user consent.
131 // For now, we treat this as an automatic allow.
133 return PermissionsData::ACCESS_ALLOWED
;
135 GURL effective_document_url
= ScriptContext::GetEffectiveDocumentURL(
136 web_frame
, web_frame
->document().url(), script_
->match_about_blank());
137 return injection_host
->CanExecuteOnFrame(
138 effective_document_url
, top_url
, tab_id
, is_declarative_
);
141 std::vector
<blink::WebScriptSource
> UserScriptInjector::GetJsSources(
142 UserScript::RunLocation run_location
) const {
143 DCHECK_EQ(script_
->run_location(), run_location
);
145 std::vector
<blink::WebScriptSource
> sources
;
146 const UserScript::FileList
& js_scripts
= script_
->js_scripts();
147 bool is_standalone_or_emulate_greasemonkey
=
148 script_
->is_standalone() || script_
->emulate_greasemonkey();
150 for (UserScript::FileList::const_iterator iter
= js_scripts
.begin();
151 iter
!= js_scripts
.end();
153 std::string content
= iter
->GetContent().as_string();
155 // We add this dumb function wrapper for standalone user script to
156 // emulate what Greasemonkey does.
157 // TODO(aa): I think that maybe "is_standalone" scripts don't exist
158 // anymore. Investigate.
159 if (is_standalone_or_emulate_greasemonkey
) {
160 content
.insert(0, kUserScriptHead
);
161 content
+= kUserScriptTail
;
163 sources
.push_back(blink::WebScriptSource(
164 blink::WebString::fromUTF8(content
), iter
->url()));
167 // Emulate Greasemonkey API for scripts that were converted to extensions
168 // and "standalone" user scripts.
169 if (is_standalone_or_emulate_greasemonkey
)
170 sources
.insert(sources
.begin(), g_greasemonkey_api
.Get().GetSource());
175 std::vector
<std::string
> UserScriptInjector::GetCssSources(
176 UserScript::RunLocation run_location
) const {
177 DCHECK_EQ(UserScript::DOCUMENT_START
, run_location
);
179 std::vector
<std::string
> sources
;
180 const UserScript::FileList
& css_scripts
= script_
->css_scripts();
181 for (UserScript::FileList::const_iterator iter
= css_scripts
.begin();
182 iter
!= css_scripts
.end();
184 sources
.push_back(iter
->GetContent().as_string());
189 void UserScriptInjector::OnInjectionComplete(
190 scoped_ptr
<base::ListValue
> execution_results
,
191 ScriptsRunInfo
* scripts_run_info
,
192 UserScript::RunLocation run_location
) {
193 if (ShouldInjectJs(run_location
)) {
194 const UserScript::FileList
& js_scripts
= script_
->js_scripts();
195 scripts_run_info
->num_js
+= js_scripts
.size();
196 for (UserScript::FileList::const_iterator iter
= js_scripts
.begin();
197 iter
!= js_scripts
.end();
199 scripts_run_info
->executing_scripts
[extension_id_
].insert(
204 if (ShouldInjectCss(run_location
))
205 scripts_run_info
->num_css
+= script_
->css_scripts().size();
208 void UserScriptInjector::OnWillNotInject(InjectFailureReason reason
) {
211 } // namespace extensions