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/browser/script_executor.h"
8 #include "base/callback.h"
9 #include "base/logging.h"
10 #include "base/pickle.h"
11 #include "content/public/browser/render_frame_host.h"
12 #include "content/public/browser/render_view_host.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/browser/web_contents_observer.h"
15 #include "extensions/browser/extension_registry.h"
16 #include "extensions/browser/script_execution_observer.h"
17 #include "extensions/common/extension_messages.h"
18 #include "ipc/ipc_message.h"
19 #include "ipc/ipc_message_macros.h"
25 namespace extensions
{
29 const char* kRendererDestroyed
= "The tab was closed.";
31 // A handler for a single injection request. On creation this will send the
32 // injection request to the renderer, and it will be destroyed after either the
33 // corresponding response comes from the renderer, or the renderer is destroyed.
34 class Handler
: public content::WebContentsObserver
{
36 Handler(base::ObserverList
<ScriptExecutionObserver
>* script_observers
,
37 content::WebContents
* web_contents
,
38 const ExtensionMsg_ExecuteCode_Params
& params
,
39 ScriptExecutor::FrameScope scope
,
40 const ScriptExecutor::ExecuteScriptCallback
& callback
)
41 : content::WebContentsObserver(web_contents
),
42 script_observers_(AsWeakPtr(script_observers
)),
43 host_id_(params
.host_id
),
44 request_id_(params
.request_id
),
46 if (scope
== ScriptExecutor::ALL_FRAMES
) {
47 web_contents
->ForEachFrame(base::Bind(&Handler::SendExecuteCode
,
48 base::Unretained(this), params
));
50 SendExecuteCode(params
, web_contents
->GetMainFrame());
55 // This class manages its own lifetime.
56 ~Handler() override
{}
58 // content::WebContentsObserver:
59 void WebContentsDestroyed() override
{ Finish(); }
61 bool OnMessageReceived(const IPC::Message
& message
,
62 content::RenderFrameHost
* render_frame_host
) override
{
63 // Unpack by hand to check the request_id, since there may be multiple
64 // requests in flight but only one is for this.
65 if (message
.type() != ExtensionHostMsg_ExecuteCodeFinished::ID
)
68 int message_request_id
;
69 base::PickleIterator
iter(message
);
70 CHECK(iter
.ReadInt(&message_request_id
));
72 if (message_request_id
!= request_id_
)
75 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(Handler
, message
, render_frame_host
)
76 IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished
,
77 OnExecuteCodeFinished
)
82 void RenderFrameDeleted(
83 content::RenderFrameHost
* render_frame_host
) override
{
84 if (pending_render_frames_
.erase(render_frame_host
) == 1 &&
85 pending_render_frames_
.empty()) {
90 // Sends an ExecuteCode message to the given frame host, and increments
91 // the number of pending messages.
92 void SendExecuteCode(const ExtensionMsg_ExecuteCode_Params
& params
,
93 content::RenderFrameHost
* frame
) {
94 pending_render_frames_
.insert(frame
);
95 frame
->Send(new ExtensionMsg_ExecuteCode(frame
->GetRoutingID(), params
));
98 // Handles the ExecuteCodeFinished message.
99 void OnExecuteCodeFinished(content::RenderFrameHost
* render_frame_host
,
101 const std::string
& error
,
103 const base::ListValue
& result_list
) {
104 DCHECK_EQ(request_id_
, request_id
);
105 DCHECK(!pending_render_frames_
.empty());
106 bool erased
= pending_render_frames_
.erase(render_frame_host
) == 1;
108 bool is_main_frame
= web_contents()->GetMainFrame() == render_frame_host
;
110 // Set the result, if there is one.
111 const base::Value
* script_value
= nullptr;
112 if (result_list
.Get(0u, &script_value
)) {
113 // If this is the main result, we put it at index 0. Otherwise, we just
114 // append it at the end.
115 if (is_main_frame
&& !results_
.empty())
116 CHECK(results_
.Insert(0u, script_value
->DeepCopy()));
118 results_
.Append(script_value
->DeepCopy());
121 if (is_main_frame
) { // Only use the main frame's error and url.
122 main_frame_error_
= error
;
123 main_frame_url_
= on_url
;
126 // Wait until the final request finishes before reporting back.
127 if (pending_render_frames_
.empty())
132 if (main_frame_url_
.is_empty()) {
133 // We never finished the main frame injection.
134 main_frame_error_
= kRendererDestroyed
;
138 if (script_observers_
.get() && main_frame_error_
.empty() &&
139 host_id_
.type() == HostID::EXTENSIONS
) {
140 ScriptExecutionObserver::ExecutingScriptsMap id_map
;
141 id_map
[host_id_
.id()] = std::set
<std::string
>();
143 ScriptExecutionObserver
, *script_observers_
,
144 OnScriptsExecuted(web_contents(), id_map
, main_frame_url_
));
147 if (!callback_
.is_null())
148 callback_
.Run(main_frame_error_
, main_frame_url_
, results_
);
152 base::WeakPtr
<base::ObserverList
<ScriptExecutionObserver
>> script_observers_
;
154 // The id of the host (the extension or the webui) doing the injection.
157 // The request id of the injection.
160 // The hosts of the still-running injections.
161 std::set
<content::RenderFrameHost
*> pending_render_frames_
;
163 // The results of the injection.
164 base::ListValue results_
;
166 // The error from injecting into the main frame.
167 std::string main_frame_error_
;
169 // The url of the main frame.
170 GURL main_frame_url_
;
172 // The callback to run after all injections complete.
173 ScriptExecutor::ExecuteScriptCallback callback_
;
175 DISALLOW_COPY_AND_ASSIGN(Handler
);
180 ScriptExecutionObserver::~ScriptExecutionObserver() {
183 ScriptExecutor::ScriptExecutor(
184 content::WebContents
* web_contents
,
185 base::ObserverList
<ScriptExecutionObserver
>* script_observers
)
186 : next_request_id_(0),
187 web_contents_(web_contents
),
188 script_observers_(script_observers
) {
189 CHECK(web_contents_
);
192 ScriptExecutor::~ScriptExecutor() {
195 void ScriptExecutor::ExecuteScript(const HostID
& host_id
,
196 ScriptExecutor::ScriptType script_type
,
197 const std::string
& code
,
198 ScriptExecutor::FrameScope frame_scope
,
199 ScriptExecutor::MatchAboutBlank about_blank
,
200 UserScript::RunLocation run_at
,
201 ScriptExecutor::WorldType world_type
,
202 ScriptExecutor::ProcessType process_type
,
203 const GURL
& webview_src
,
204 const GURL
& file_url
,
206 ScriptExecutor::ResultType result_type
,
207 const ExecuteScriptCallback
& callback
) {
208 if (host_id
.type() == HostID::EXTENSIONS
) {
209 // Don't execute if the extension has been unloaded.
210 const Extension
* extension
=
211 ExtensionRegistry::Get(web_contents_
->GetBrowserContext())
212 ->enabled_extensions().GetByID(host_id
.id());
216 CHECK(process_type
== WEB_VIEW_PROCESS
);
219 ExtensionMsg_ExecuteCode_Params params
;
220 params
.request_id
= next_request_id_
++;
221 params
.host_id
= host_id
;
222 params
.is_javascript
= (script_type
== JAVASCRIPT
);
224 params
.match_about_blank
= (about_blank
== MATCH_ABOUT_BLANK
);
225 params
.run_at
= static_cast<int>(run_at
);
226 params
.in_main_world
= (world_type
== MAIN_WORLD
);
227 params
.is_web_view
= (process_type
== WEB_VIEW_PROCESS
);
228 params
.webview_src
= webview_src
;
229 params
.file_url
= file_url
;
230 params
.wants_result
= (result_type
== JSON_SERIALIZED_RESULT
);
231 params
.user_gesture
= user_gesture
;
233 // Handler handles IPCs and deletes itself on completion.
234 new Handler(script_observers_
, web_contents_
, params
, frame_scope
, callback
);
237 } // namespace extensions