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/programmatic_script_injector.h"
9 #include "base/values.h"
10 #include "content/public/common/url_constants.h"
11 #include "content/public/renderer/render_frame.h"
12 #include "content/public/renderer/render_frame_observer.h"
13 #include "extensions/common/error_utils.h"
14 #include "extensions/common/extension_messages.h"
15 #include "extensions/common/manifest_constants.h"
16 #include "extensions/common/permissions/permissions_data.h"
17 #include "extensions/renderer/injection_host.h"
18 #include "extensions/renderer/script_context.h"
19 #include "third_party/WebKit/public/platform/WebString.h"
20 #include "third_party/WebKit/public/web/WebDocument.h"
21 #include "third_party/WebKit/public/web/WebLocalFrame.h"
22 #include "third_party/WebKit/public/web/WebScriptSource.h"
24 namespace extensions
{
26 // Watches for the deletion of a RenderFrame, after which is_valid will return
28 class ProgrammaticScriptInjector::FrameWatcher
29 : public content::RenderFrameObserver
{
31 explicit FrameWatcher(content::RenderFrame
* render_frame
)
32 : content::RenderFrameObserver(render_frame
), is_valid_(true) {}
33 ~FrameWatcher() override
{}
35 bool is_frame_valid() const { return is_valid_
; }
38 void FrameDetached() override
{ is_valid_
= false; }
39 void OnDestruct() override
{ is_valid_
= false; }
43 DISALLOW_COPY_AND_ASSIGN(FrameWatcher
);
46 ProgrammaticScriptInjector::ProgrammaticScriptInjector(
47 const ExtensionMsg_ExecuteCode_Params
& params
,
48 content::RenderFrame
* render_frame
)
49 : params_(new ExtensionMsg_ExecuteCode_Params(params
)),
51 ScriptContext::GetDataSourceURLForFrame(render_frame
->GetWebFrame())),
52 frame_watcher_(new FrameWatcher(render_frame
)),
54 effective_url_
= ScriptContext::GetEffectiveDocumentURL(
55 render_frame
->GetWebFrame(), url_
, params
.match_about_blank
);
58 ProgrammaticScriptInjector::~ProgrammaticScriptInjector() {
61 UserScript::InjectionType
ProgrammaticScriptInjector::script_type()
63 return UserScript::PROGRAMMATIC_SCRIPT
;
66 bool ProgrammaticScriptInjector::ShouldExecuteInMainWorld() const {
67 return params_
->in_main_world
;
70 bool ProgrammaticScriptInjector::IsUserGesture() const {
71 return params_
->user_gesture
;
74 bool ProgrammaticScriptInjector::ExpectsResults() const {
75 return params_
->wants_result
;
78 bool ProgrammaticScriptInjector::ShouldInjectJs(
79 UserScript::RunLocation run_location
) const {
80 return GetRunLocation() == run_location
&& params_
->is_javascript
;
83 bool ProgrammaticScriptInjector::ShouldInjectCss(
84 UserScript::RunLocation run_location
) const {
85 return GetRunLocation() == run_location
&& !params_
->is_javascript
;
88 PermissionsData::AccessType
ProgrammaticScriptInjector::CanExecuteOnFrame(
89 const InjectionHost
* injection_host
,
90 blink::WebLocalFrame
* frame
,
92 GURL effective_document_url
= ScriptContext::GetEffectiveDocumentURL(
93 frame
, frame
->document().url(), params_
->match_about_blank
);
94 if (params_
->is_web_view
) {
95 if (frame
->parent()) {
96 // This is a subframe inside <webview>, so allow it.
97 return PermissionsData::ACCESS_ALLOWED
;
100 return effective_document_url
== params_
->webview_src
101 ? PermissionsData::ACCESS_ALLOWED
102 : PermissionsData::ACCESS_DENIED
;
104 DCHECK_EQ(injection_host
->id().type(), HostID::EXTENSIONS
);
106 return injection_host
->CanExecuteOnFrame(
107 effective_document_url
,
108 content::RenderFrame::FromWebFrame(frame
),
110 true /* is_declarative */);
113 std::vector
<blink::WebScriptSource
> ProgrammaticScriptInjector::GetJsSources(
114 UserScript::RunLocation run_location
) const {
115 DCHECK_EQ(GetRunLocation(), run_location
);
116 DCHECK(params_
->is_javascript
);
118 return std::vector
<blink::WebScriptSource
>(
120 blink::WebScriptSource(
121 blink::WebString::fromUTF8(params_
->code
), params_
->file_url
));
124 std::vector
<std::string
> ProgrammaticScriptInjector::GetCssSources(
125 UserScript::RunLocation run_location
) const {
126 DCHECK_EQ(GetRunLocation(), run_location
);
127 DCHECK(!params_
->is_javascript
);
129 return std::vector
<std::string
>(1, params_
->code
);
132 void ProgrammaticScriptInjector::GetRunInfo(
133 ScriptsRunInfo
* scripts_run_info
,
134 UserScript::RunLocation run_location
) const {
137 void ProgrammaticScriptInjector::OnInjectionComplete(
138 scoped_ptr
<base::Value
> execution_result
,
139 UserScript::RunLocation run_location
) {
140 DCHECK(results_
.empty());
141 if (execution_result
)
142 results_
.Append(execution_result
.Pass());
143 Finish(std::string());
146 void ProgrammaticScriptInjector::OnWillNotInject(InjectFailureReason reason
) {
150 if (url_
.SchemeIs(url::kAboutScheme
)) {
151 error
= ErrorUtils::FormatErrorMessage(
152 manifest_errors::kCannotAccessAboutUrl
, url_
.spec(),
153 effective_url_
.GetOrigin().spec());
155 error
= ErrorUtils::FormatErrorMessage(
156 manifest_errors::kCannotAccessPage
, url_
.spec());
159 case EXTENSION_REMOVED
: // no special error here.
166 UserScript::RunLocation
ProgrammaticScriptInjector::GetRunLocation() const {
167 return static_cast<UserScript::RunLocation
>(params_
->run_at
);
170 void ProgrammaticScriptInjector::Finish(const std::string
& error
) {
174 // It's possible that the render frame was destroyed in the course of
175 // injecting scripts. Don't respond if it was (the browser side watches for
176 // frame deletions so nothing is left hanging).
177 if (frame_watcher_
->is_frame_valid()) {
178 frame_watcher_
->render_frame()->Send(
179 new ExtensionHostMsg_ExecuteCodeFinished(
180 frame_watcher_
->render_frame()->GetRoutingID(), params_
->request_id
,
181 error
, url_
, results_
));
185 } // namespace extensions