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 "content/public/common/url_constants.h"
8 #include "content/public/renderer/render_thread.h"
9 #include "extensions/common/extension.h"
10 #include "extensions/common/extension_set.h"
11 #include "extensions/common/permissions/permissions_data.h"
12 #include "extensions/renderer/extensions_renderer_client.h"
13 #include "extensions/renderer/script_context.h"
14 #include "extensions/renderer/script_injection.h"
15 #include "extensions/renderer/user_script_injector.h"
16 #include "third_party/WebKit/public/web/WebDocument.h"
17 #include "third_party/WebKit/public/web/WebFrame.h"
20 namespace extensions
{
24 GURL
GetDocumentUrlForFrame(blink::WebFrame
* frame
) {
25 GURL data_source_url
= ScriptContext::GetDataSourceURLForFrame(frame
);
26 if (!data_source_url
.is_empty() && frame
->isViewSourceModeEnabled()) {
27 data_source_url
= GURL(content::kViewSourceScheme
+ std::string(":") +
28 data_source_url
.spec());
31 return data_source_url
;
36 UserScriptSet::UserScriptSet(const ExtensionSet
* extensions
)
37 : extensions_(extensions
) {
40 UserScriptSet::~UserScriptSet() {
43 void UserScriptSet::AddObserver(Observer
* observer
) {
44 observers_
.AddObserver(observer
);
47 void UserScriptSet::RemoveObserver(Observer
* observer
) {
48 observers_
.RemoveObserver(observer
);
51 void UserScriptSet::GetActiveExtensionIds(
52 std::set
<std::string
>* ids
) const {
53 for (ScopedVector
<UserScript
>::const_iterator iter
= scripts_
.begin();
54 iter
!= scripts_
.end();
56 DCHECK(!(*iter
)->extension_id().empty());
57 ids
->insert((*iter
)->extension_id());
61 void UserScriptSet::GetInjections(
62 ScopedVector
<ScriptInjection
>* injections
,
63 blink::WebFrame
* web_frame
,
65 UserScript::RunLocation run_location
) {
66 GURL document_url
= GetDocumentUrlForFrame(web_frame
);
67 for (ScopedVector
<UserScript
>::const_iterator iter
= scripts_
.begin();
68 iter
!= scripts_
.end();
70 const Extension
* extension
= extensions_
->GetByID((*iter
)->extension_id());
73 scoped_ptr
<ScriptInjection
> injection
= GetInjectionForScript(
80 false /* is_declarative */);
82 injections
->push_back(injection
.release());
86 bool UserScriptSet::UpdateUserScripts(
87 base::SharedMemoryHandle shared_memory
,
88 const std::set
<std::string
>& changed_extensions
) {
89 bool only_inject_incognito
=
90 ExtensionsRendererClient::Get()->IsIncognitoProcess();
92 // Create the shared memory object (read only).
93 shared_memory_
.reset(new base::SharedMemory(shared_memory
, true));
94 if (!shared_memory_
.get())
97 // First get the size of the memory block.
98 if (!shared_memory_
->Map(sizeof(Pickle::Header
)))
100 Pickle::Header
* pickle_header
=
101 reinterpret_cast<Pickle::Header
*>(shared_memory_
->memory());
103 // Now map in the rest of the block.
104 int pickle_size
= sizeof(Pickle::Header
) + pickle_header
->payload_size
;
105 shared_memory_
->Unmap();
106 if (!shared_memory_
->Map(pickle_size
))
110 size_t num_scripts
= 0;
111 Pickle
pickle(reinterpret_cast<char*>(shared_memory_
->memory()), pickle_size
);
112 PickleIterator
iter(pickle
);
113 CHECK(pickle
.ReadSizeT(&iter
, &num_scripts
));
116 scripts_
.reserve(num_scripts
);
117 for (size_t i
= 0; i
< num_scripts
; ++i
) {
118 scoped_ptr
<UserScript
> script(new UserScript());
119 script
->Unpickle(pickle
, &iter
);
121 // Note that this is a pointer into shared memory. We don't own it. It gets
122 // cleared up when the last renderer or browser process drops their
123 // reference to the shared memory.
124 for (size_t j
= 0; j
< script
->js_scripts().size(); ++j
) {
125 const char* body
= NULL
;
127 CHECK(pickle
.ReadData(&iter
, &body
, &body_length
));
128 script
->js_scripts()[j
].set_external_content(
129 base::StringPiece(body
, body_length
));
131 for (size_t j
= 0; j
< script
->css_scripts().size(); ++j
) {
132 const char* body
= NULL
;
134 CHECK(pickle
.ReadData(&iter
, &body
, &body_length
));
135 script
->css_scripts()[j
].set_external_content(
136 base::StringPiece(body
, body_length
));
139 if (only_inject_incognito
&& !script
->is_incognito_enabled())
140 continue; // This script shouldn't run in an incognito tab.
142 scripts_
.push_back(script
.release());
145 FOR_EACH_OBSERVER(Observer
,
147 OnUserScriptsUpdated(changed_extensions
, scripts_
.get()));
151 scoped_ptr
<ScriptInjection
> UserScriptSet::GetDeclarativeScriptInjection(
153 blink::WebFrame
* web_frame
,
155 UserScript::RunLocation run_location
,
156 const GURL
& document_url
,
157 const Extension
* extension
) {
158 for (ScopedVector
<UserScript
>::const_iterator it
= scripts_
.begin();
159 it
!= scripts_
.end();
161 if ((*it
)->id() == script_id
) {
162 return GetInjectionForScript(*it
,
168 true /* is_declarative */);
171 return scoped_ptr
<ScriptInjection
>();
174 // TODO(dcheng): Scripts can't be injected on a remote frame, so this function
175 // signature needs to be updated.
176 scoped_ptr
<ScriptInjection
> UserScriptSet::GetInjectionForScript(
178 blink::WebFrame
* web_frame
,
180 UserScript::RunLocation run_location
,
181 const GURL
& document_url
,
182 const Extension
* extension
,
183 bool is_declarative
) {
184 scoped_ptr
<ScriptInjection
> injection
;
185 if (web_frame
->parent() && !script
->match_all_frames())
186 return injection
.Pass(); // Only match subframes if the script declared it.
188 GURL effective_document_url
= ScriptContext::GetEffectiveDocumentURL(
189 web_frame
, document_url
, script
->match_about_blank());
191 if (!script
->MatchesURL(effective_document_url
))
192 return injection
.Pass();
194 scoped_ptr
<ScriptInjector
> injector(new UserScriptInjector(script
,
197 if (injector
->CanExecuteOnFrame(
200 -1, // Content scripts are not tab-specific.
201 web_frame
->top()->document().url()) ==
202 PermissionsData::ACCESS_DENIED
) {
203 return injection
.Pass();
206 bool inject_css
= !script
->css_scripts().empty() &&
207 run_location
== UserScript::DOCUMENT_START
;
209 !script
->js_scripts().empty() && script
->run_location() == run_location
;
210 if (inject_css
|| inject_js
) {
211 injection
.reset(new ScriptInjection(
213 web_frame
->toWebLocalFrame(),
218 return injection
.Pass();
221 } // namespace extensions