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/extension_helper.h"
7 #include "base/lazy_instance.h"
8 #include "content/public/renderer/render_view.h"
9 #include "content/public/renderer/render_view_visitor.h"
10 #include "extensions/common/api/messaging/message.h"
11 #include "extensions/common/constants.h"
12 #include "extensions/common/extension_messages.h"
13 #include "extensions/renderer/console.h"
14 #include "extensions/renderer/dispatcher.h"
15 #include "extensions/renderer/messaging_bindings.h"
16 #include "extensions/renderer/user_script_scheduler.h"
17 #include "extensions/renderer/user_script_slave.h"
18 #include "third_party/WebKit/public/platform/WebURLRequest.h"
19 #include "third_party/WebKit/public/web/WebConsoleMessage.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/WebScopedUserGesture.h"
23 #include "third_party/WebKit/public/web/WebView.h"
25 using content::ConsoleMessageLevel
;
26 using blink::WebConsoleMessage
;
27 using blink::WebDataSource
;
28 using blink::WebFrame
;
29 using blink::WebLocalFrame
;
30 using blink::WebURLRequest
;
31 using blink::WebScopedUserGesture
;
34 namespace extensions
{
37 // Keeps a mapping from the frame pointer to a UserScriptScheduler object.
38 // We store this mapping per process, because a frame can jump from one
39 // document to another with adoptNode, and so having the object be a
40 // RenderViewObserver means it might miss some notifications after it moves.
41 typedef std::map
<WebFrame
*, UserScriptScheduler
*> SchedulerMap
;
42 static base::LazyInstance
<SchedulerMap
> g_schedulers
=
43 LAZY_INSTANCE_INITIALIZER
;
45 // A RenderViewVisitor class that iterates through the set of available
46 // views, looking for a view of the given type, in the given browser window
47 // and within the given extension.
48 // Used to accumulate the list of views associated with an extension.
49 class ViewAccumulator
: public content::RenderViewVisitor
{
51 ViewAccumulator(const std::string
& extension_id
,
52 int browser_window_id
,
54 : extension_id_(extension_id
),
55 browser_window_id_(browser_window_id
),
56 view_type_(view_type
) {
59 std::vector
<content::RenderView
*> views() { return views_
; }
61 // Returns false to terminate the iteration.
62 virtual bool Visit(content::RenderView
* render_view
) OVERRIDE
{
63 ExtensionHelper
* helper
= ExtensionHelper::Get(render_view
);
64 if (!ViewTypeMatches(helper
->view_type(), view_type_
))
67 GURL url
= render_view
->GetWebView()->mainFrame()->document().url();
68 if (!url
.SchemeIs(kExtensionScheme
))
70 const std::string
& extension_id
= url
.host();
71 if (extension_id
!= extension_id_
)
74 if (browser_window_id_
!= extension_misc::kUnknownWindowId
&&
75 helper
->browser_window_id() != browser_window_id_
) {
79 views_
.push_back(render_view
);
81 if (view_type_
== VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
)
82 return false; // There can be only one...
87 // Returns true if |type| "isa" |match|.
88 static bool ViewTypeMatches(ViewType type
, ViewType match
) {
92 // INVALID means match all.
93 if (match
== VIEW_TYPE_INVALID
)
99 std::string extension_id_
;
100 int browser_window_id_
;
102 std::vector
<content::RenderView
*> views_
;
108 std::vector
<content::RenderView
*> ExtensionHelper::GetExtensionViews(
109 const std::string
& extension_id
,
110 int browser_window_id
,
111 ViewType view_type
) {
112 ViewAccumulator
accumulator(extension_id
, browser_window_id
, view_type
);
113 content::RenderView::ForEach(&accumulator
);
114 return accumulator
.views();
118 content::RenderView
* ExtensionHelper::GetBackgroundPage(
119 const std::string
& extension_id
) {
120 ViewAccumulator
accumulator(extension_id
, extension_misc::kUnknownWindowId
,
121 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE
);
122 content::RenderView::ForEach(&accumulator
);
123 CHECK_LE(accumulator
.views().size(), 1u);
124 if (accumulator
.views().size() == 0)
126 return accumulator
.views()[0];
129 ExtensionHelper::ExtensionHelper(content::RenderView
* render_view
,
130 Dispatcher
* dispatcher
)
131 : content::RenderViewObserver(render_view
),
132 content::RenderViewObserverTracker
<ExtensionHelper
>(render_view
),
133 dispatcher_(dispatcher
),
134 view_type_(VIEW_TYPE_INVALID
),
136 browser_window_id_(-1) {
139 ExtensionHelper::~ExtensionHelper() {
142 bool ExtensionHelper::OnMessageReceived(const IPC::Message
& message
) {
144 IPC_BEGIN_MESSAGE_MAP(ExtensionHelper
, message
)
145 IPC_MESSAGE_HANDLER(ExtensionMsg_Response
, OnExtensionResponse
)
146 IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke
, OnExtensionMessageInvoke
)
147 IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect
,
148 OnExtensionDispatchOnConnect
)
149 IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage
, OnExtensionDeliverMessage
)
150 IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect
,
151 OnExtensionDispatchOnDisconnect
)
152 IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode
, OnExecuteCode
)
153 IPC_MESSAGE_HANDLER(ExtensionMsg_SetTabId
, OnSetTabId
)
154 IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateBrowserWindowId
,
155 OnUpdateBrowserWindowId
)
156 IPC_MESSAGE_HANDLER(ExtensionMsg_NotifyRenderViewType
,
157 OnNotifyRendererViewType
)
158 IPC_MESSAGE_HANDLER(ExtensionMsg_AddMessageToConsole
,
159 OnAddMessageToConsole
)
160 IPC_MESSAGE_HANDLER(ExtensionMsg_AppWindowClosed
,
162 IPC_MESSAGE_UNHANDLED(handled
= false)
163 IPC_END_MESSAGE_MAP()
167 void ExtensionHelper::DidFinishDocumentLoad(WebLocalFrame
* frame
) {
168 dispatcher_
->user_script_slave()->InjectScripts(
169 frame
, UserScript::DOCUMENT_END
);
171 SchedulerMap::iterator i
= g_schedulers
.Get().find(frame
);
172 if (i
!= g_schedulers
.Get().end())
173 i
->second
->DidFinishDocumentLoad();
176 void ExtensionHelper::DidFinishLoad(blink::WebLocalFrame
* frame
) {
177 SchedulerMap::iterator i
= g_schedulers
.Get().find(frame
);
178 if (i
!= g_schedulers
.Get().end())
179 i
->second
->DidFinishLoad();
182 void ExtensionHelper::DidCreateDocumentElement(WebLocalFrame
* frame
) {
183 dispatcher_
->user_script_slave()->InjectScripts(
184 frame
, UserScript::DOCUMENT_START
);
185 SchedulerMap::iterator i
= g_schedulers
.Get().find(frame
);
186 if (i
!= g_schedulers
.Get().end())
187 i
->second
->DidCreateDocumentElement();
189 dispatcher_
->DidCreateDocumentElement(frame
);
192 void ExtensionHelper::DidStartProvisionalLoad(blink::WebLocalFrame
* frame
) {
193 SchedulerMap::iterator i
= g_schedulers
.Get().find(frame
);
194 if (i
!= g_schedulers
.Get().end())
195 i
->second
->DidStartProvisionalLoad();
198 void ExtensionHelper::DraggableRegionsChanged(blink::WebFrame
* frame
) {
199 blink::WebVector
<blink::WebDraggableRegion
> webregions
=
200 frame
->document().draggableRegions();
201 std::vector
<DraggableRegion
> regions
;
202 for (size_t i
= 0; i
< webregions
.size(); ++i
) {
203 DraggableRegion region
;
204 region
.bounds
= webregions
[i
].bounds
;
205 region
.draggable
= webregions
[i
].draggable
;
206 regions
.push_back(region
);
208 Send(new ExtensionHostMsg_UpdateDraggableRegions(routing_id(), regions
));
211 void ExtensionHelper::FrameDetached(WebFrame
* frame
) {
212 // This could be called before DidCreateDataSource, in which case the frame
213 // won't be in the map.
214 SchedulerMap::iterator i
= g_schedulers
.Get().find(frame
);
215 if (i
== g_schedulers
.Get().end())
219 g_schedulers
.Get().erase(i
);
222 void ExtensionHelper::DidMatchCSS(
223 blink::WebLocalFrame
* frame
,
224 const blink::WebVector
<blink::WebString
>& newly_matching_selectors
,
225 const blink::WebVector
<blink::WebString
>& stopped_matching_selectors
) {
226 dispatcher_
->DidMatchCSS(
227 frame
, newly_matching_selectors
, stopped_matching_selectors
);
230 void ExtensionHelper::DidCreateDataSource(WebLocalFrame
* frame
,
232 // Check first if we created a scheduler for the frame, since this function
233 // gets called for navigations within the document.
234 if (g_schedulers
.Get().count(frame
))
237 g_schedulers
.Get()[frame
] = new UserScriptScheduler(frame
, dispatcher_
);
240 void ExtensionHelper::OnExtensionResponse(int request_id
,
242 const base::ListValue
& response
,
243 const std::string
& error
) {
244 dispatcher_
->OnExtensionResponse(request_id
,
250 void ExtensionHelper::OnExtensionMessageInvoke(const std::string
& extension_id
,
251 const std::string
& module_name
,
252 const std::string
& function_name
,
253 const base::ListValue
& args
,
255 dispatcher_
->InvokeModuleSystemMethod(
256 render_view(), extension_id
, module_name
, function_name
, args
,
260 void ExtensionHelper::OnExtensionDispatchOnConnect(
262 const std::string
& channel_name
,
263 const base::DictionaryValue
& source_tab
,
264 const ExtensionMsg_ExternalConnectionInfo
& info
,
265 const std::string
& tls_channel_id
) {
266 MessagingBindings::DispatchOnConnect(
267 dispatcher_
->script_context_set().GetAll(),
278 void ExtensionHelper::OnExtensionDeliverMessage(int target_id
,
279 const Message
& message
) {
280 MessagingBindings::DeliverMessage(dispatcher_
->script_context_set().GetAll(),
286 void ExtensionHelper::OnExtensionDispatchOnDisconnect(
288 const std::string
& error_message
) {
289 MessagingBindings::DispatchOnDisconnect(
290 dispatcher_
->script_context_set().GetAll(),
296 void ExtensionHelper::OnExecuteCode(
297 const ExtensionMsg_ExecuteCode_Params
& params
) {
298 WebView
* webview
= render_view()->GetWebView();
299 WebFrame
* main_frame
= webview
->mainFrame();
302 Send(new ExtensionHostMsg_ExecuteCodeFinished(routing_id(),
311 // chrome.tabs.executeScript() only supports execution in either the top frame
312 // or all frames. We handle both cases in the top frame.
313 SchedulerMap::iterator i
= g_schedulers
.Get().find(main_frame
);
314 if (i
!= g_schedulers
.Get().end())
315 i
->second
->ExecuteCode(params
);
318 void ExtensionHelper::OnNotifyRendererViewType(ViewType type
) {
322 void ExtensionHelper::OnSetTabId(int init_tab_id
) {
323 CHECK_EQ(tab_id_
, -1);
324 CHECK_GE(init_tab_id
, 0);
325 tab_id_
= init_tab_id
;
328 void ExtensionHelper::OnUpdateBrowserWindowId(int window_id
) {
329 browser_window_id_
= window_id
;
332 void ExtensionHelper::OnAddMessageToConsole(ConsoleMessageLevel level
,
333 const std::string
& message
) {
334 console::AddMessage(render_view(), level
, message
);
337 void ExtensionHelper::OnAppWindowClosed() {
338 v8::HandleScope
scope(v8::Isolate::GetCurrent());
339 v8::Handle
<v8::Context
> v8_context
=
340 render_view()->GetWebView()->mainFrame()->mainWorldScriptContext();
341 ScriptContext
* script_context
=
342 dispatcher_
->script_context_set().GetByV8Context(v8_context
);
345 script_context
->module_system()->CallModuleMethod("app.window",
346 "onAppWindowClosed");
349 } // namespace extensions