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_set.h"
7 #include "base/memory/ref_counted.h"
8 #include "content/public/common/url_constants.h"
9 #include "content/public/renderer/render_frame.h"
10 #include "content/public/renderer/render_thread.h"
11 #include "extensions/common/extension.h"
12 #include "extensions/common/extension_set.h"
13 #include "extensions/common/extensions_client.h"
14 #include "extensions/common/permissions/permissions_data.h"
15 #include "extensions/renderer/extension_injection_host.h"
16 #include "extensions/renderer/extensions_renderer_client.h"
17 #include "extensions/renderer/injection_host.h"
18 #include "extensions/renderer/script_context.h"
19 #include "extensions/renderer/script_injection.h"
20 #include "extensions/renderer/user_script_injector.h"
21 #include "extensions/renderer/web_ui_injection_host.h"
22 #include "third_party/WebKit/public/web/WebDocument.h"
23 #include "third_party/WebKit/public/web/WebFrame.h"
24 #include "third_party/WebKit/public/web/WebLocalFrame.h"
27 namespace extensions
{
31 GURL
GetDocumentUrlForFrame(blink::WebLocalFrame
* frame
) {
32 GURL data_source_url
= ScriptContext::GetDataSourceURLForFrame(frame
);
33 if (!data_source_url
.is_empty() && frame
->isViewSourceModeEnabled()) {
34 data_source_url
= GURL(content::kViewSourceScheme
+ std::string(":") +
35 data_source_url
.spec());
38 return data_source_url
;
43 UserScriptSet::UserScriptSet(const ExtensionSet
* extensions
)
44 : extensions_(extensions
) {
47 UserScriptSet::~UserScriptSet() {
50 void UserScriptSet::AddObserver(Observer
* observer
) {
51 observers_
.AddObserver(observer
);
54 void UserScriptSet::RemoveObserver(Observer
* observer
) {
55 observers_
.RemoveObserver(observer
);
58 void UserScriptSet::GetActiveExtensionIds(
59 std::set
<std::string
>* ids
) const {
60 for (ScopedVector
<UserScript
>::const_iterator iter
= scripts_
.begin();
61 iter
!= scripts_
.end();
63 if ((*iter
)->host_id().type() != HostID::EXTENSIONS
)
65 DCHECK(!(*iter
)->extension_id().empty());
66 ids
->insert((*iter
)->extension_id());
70 void UserScriptSet::GetInjections(
71 ScopedVector
<ScriptInjection
>* injections
,
72 content::RenderFrame
* render_frame
,
74 UserScript::RunLocation run_location
) {
75 GURL document_url
= GetDocumentUrlForFrame(render_frame
->GetWebFrame());
76 for (ScopedVector
<UserScript
>::const_iterator iter
= scripts_
.begin();
77 iter
!= scripts_
.end();
79 scoped_ptr
<ScriptInjection
> injection
= GetInjectionForScript(
85 false /* is_declarative */);
87 injections
->push_back(injection
.Pass());
91 bool UserScriptSet::UpdateUserScripts(base::SharedMemoryHandle shared_memory
,
92 const std::set
<HostID
>& changed_hosts
,
93 bool whitelisted_only
) {
94 bool only_inject_incognito
=
95 ExtensionsRendererClient::Get()->IsIncognitoProcess();
97 // Create the shared memory object (read only).
98 shared_memory_
.reset(new base::SharedMemory(shared_memory
, true));
99 if (!shared_memory_
.get())
102 // First get the size of the memory block.
103 if (!shared_memory_
->Map(sizeof(base::Pickle::Header
)))
105 base::Pickle::Header
* pickle_header
=
106 reinterpret_cast<base::Pickle::Header
*>(shared_memory_
->memory());
108 // Now map in the rest of the block.
109 int pickle_size
= sizeof(base::Pickle::Header
) + pickle_header
->payload_size
;
110 shared_memory_
->Unmap();
111 if (!shared_memory_
->Map(pickle_size
))
115 size_t num_scripts
= 0;
116 base::Pickle
pickle(reinterpret_cast<char*>(shared_memory_
->memory()),
118 base::PickleIterator
iter(pickle
);
119 CHECK(iter
.ReadSizeT(&num_scripts
));
122 scripts_
.reserve(num_scripts
);
123 for (size_t i
= 0; i
< num_scripts
; ++i
) {
124 scoped_ptr
<UserScript
> script(new UserScript());
125 script
->Unpickle(pickle
, &iter
);
127 // Note that this is a pointer into shared memory. We don't own it. It gets
128 // cleared up when the last renderer or browser process drops their
129 // reference to the shared memory.
130 for (size_t j
= 0; j
< script
->js_scripts().size(); ++j
) {
131 const char* body
= NULL
;
133 CHECK(iter
.ReadData(&body
, &body_length
));
134 script
->js_scripts()[j
].set_external_content(
135 base::StringPiece(body
, body_length
));
137 for (size_t j
= 0; j
< script
->css_scripts().size(); ++j
) {
138 const char* body
= NULL
;
140 CHECK(iter
.ReadData(&body
, &body_length
));
141 script
->css_scripts()[j
].set_external_content(
142 base::StringPiece(body
, body_length
));
145 if (only_inject_incognito
&& !script
->is_incognito_enabled())
146 continue; // This script shouldn't run in an incognito tab.
148 const Extension
* extension
= extensions_
->GetByID(script
->extension_id());
149 if (whitelisted_only
&&
151 !PermissionsData::CanExecuteScriptEverywhere(extension
))) {
155 scripts_
.push_back(script
.Pass());
158 FOR_EACH_OBSERVER(Observer
,
160 OnUserScriptsUpdated(changed_hosts
, scripts_
.get()));
164 scoped_ptr
<ScriptInjection
> UserScriptSet::GetDeclarativeScriptInjection(
166 content::RenderFrame
* render_frame
,
168 UserScript::RunLocation run_location
,
169 const GURL
& document_url
) {
170 for (ScopedVector
<UserScript
>::const_iterator it
= scripts_
.begin();
171 it
!= scripts_
.end();
173 if ((*it
)->id() == script_id
) {
174 return GetInjectionForScript(*it
,
179 true /* is_declarative */);
182 return scoped_ptr
<ScriptInjection
>();
185 scoped_ptr
<ScriptInjection
> UserScriptSet::GetInjectionForScript(
187 content::RenderFrame
* render_frame
,
189 UserScript::RunLocation run_location
,
190 const GURL
& document_url
,
191 bool is_declarative
) {
192 scoped_ptr
<ScriptInjection
> injection
;
193 scoped_ptr
<const InjectionHost
> injection_host
;
194 blink::WebLocalFrame
* web_frame
= render_frame
->GetWebFrame();
196 const HostID
& host_id
= script
->host_id();
197 if (host_id
.type() == HostID::EXTENSIONS
) {
198 injection_host
= ExtensionInjectionHost::Create(host_id
.id(), extensions_
);
200 return injection
.Pass();
202 DCHECK_EQ(host_id
.type(), HostID::WEBUI
);
203 injection_host
.reset(new WebUIInjectionHost(host_id
));
206 if (web_frame
->parent() && !script
->match_all_frames())
207 return injection
.Pass(); // Only match subframes if the script declared it.
209 GURL effective_document_url
= ScriptContext::GetEffectiveDocumentURL(
210 web_frame
, document_url
, script
->match_about_blank());
212 if (!script
->MatchesURL(effective_document_url
))
213 return injection
.Pass();
215 scoped_ptr
<ScriptInjector
> injector(new UserScriptInjector(script
,
219 blink::WebFrame
* top_frame
= web_frame
->top();
220 // It doesn't make sense to do script injection for remote frames, since they
221 // cannot host any documents or content.
222 // TODO(kalman): Fix this properly by moving all security checks into the
223 // browser. See http://crbug.com/466373 for ongoing work here.
224 if (top_frame
->isWebRemoteFrame())
225 return injection
.Pass();
227 if (injector
->CanExecuteOnFrame(
228 injection_host
.get(),
230 -1 /* Content scripts are not tab-specific. */) ==
231 PermissionsData::ACCESS_DENIED
) {
232 return injection
.Pass();
235 bool inject_css
= !script
->css_scripts().empty() &&
236 run_location
== UserScript::DOCUMENT_START
;
238 !script
->js_scripts().empty() && script
->run_location() == run_location
;
239 if (inject_css
|| inject_js
) {
240 injection
.reset(new ScriptInjection(
243 injection_host
.Pass(),
247 return injection
.Pass();
250 } // namespace extensions