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.
4
5 #include "extensions/renderer/extension_helper.h"
6
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"
24
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;
32 using blink::WebView;
33
34 namespace extensions {
35
36 namespace {
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;
44
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 {
50 public:
ViewAccumulator(const std::string & extension_id,int browser_window_id,ViewType view_type)51 ViewAccumulator(const std::string& extension_id,
52 int browser_window_id,
53 ViewType view_type)
54 : extension_id_(extension_id),
55 browser_window_id_(browser_window_id),
56 view_type_(view_type) {
57 }
58
views()59 std::vector<content::RenderView*> views() { return views_; }
60
61 // Returns false to terminate the iteration.
Visit(content::RenderView * render_view)62 virtual bool Visit(content::RenderView* render_view) OVERRIDE {
63 ExtensionHelper* helper = ExtensionHelper::Get(render_view);
64 if (!ViewTypeMatches(helper->view_type(), view_type_))
65 return true;
66
67 GURL url = render_view->GetWebView()->mainFrame()->document().url();
68 if (!url.SchemeIs(kExtensionScheme))
69 return true;
70 const std::string& extension_id = url.host();
71 if (extension_id != extension_id_)
72 return true;
73
74 if (browser_window_id_ != extension_misc::kUnknownWindowId &&
75 helper->browser_window_id() != browser_window_id_) {
76 return true;
77 }
78
79 views_.push_back(render_view);
80
81 if (view_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
82 return false; // There can be only one...
83 return true;
84 }
85
86 private:
87 // Returns true if |type| "isa" |match|.
ViewTypeMatches(ViewType type,ViewType match)88 static bool ViewTypeMatches(ViewType type, ViewType match) {
89 if (type == match)
90 return true;
91
92 // INVALID means match all.
93 if (match == VIEW_TYPE_INVALID)
94 return true;
95
96 return false;
97 }
98
99 std::string extension_id_;
100 int browser_window_id_;
101 ViewType view_type_;
102 std::vector<content::RenderView*> views_;
103 };
104
105 } // namespace
106
107 // static
GetExtensionViews(const std::string & extension_id,int browser_window_id,ViewType view_type)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();
115 }
116
117 // static
GetBackgroundPage(const std::string & extension_id)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)
125 return NULL;
126 return accumulator.views()[0];
127 }
128
ExtensionHelper(content::RenderView * render_view,Dispatcher * dispatcher)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),
135 tab_id_(-1),
136 browser_window_id_(-1) {
137 }
138
~ExtensionHelper()139 ExtensionHelper::~ExtensionHelper() {
140 }
141
OnMessageReceived(const IPC::Message & message)142 bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) {
143 bool handled = true;
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,
161 OnAppWindowClosed)
162 IPC_MESSAGE_HANDLER(ExtensionMsg_GrantContentScriptPermission,
163 OnGrantContentScriptPermission)
164 IPC_MESSAGE_UNHANDLED(handled = false)
165 IPC_END_MESSAGE_MAP()
166 return handled;
167 }
168
DidFinishDocumentLoad(WebLocalFrame * frame)169 void ExtensionHelper::DidFinishDocumentLoad(WebLocalFrame* frame) {
170 dispatcher_->user_script_slave()->InjectScripts(
171 frame, UserScript::DOCUMENT_END);
172
173 SchedulerMap::iterator i = g_schedulers.Get().find(frame);
174 if (i != g_schedulers.Get().end())
175 i->second->DidFinishDocumentLoad();
176 }
177
DidFinishLoad(blink::WebLocalFrame * frame)178 void ExtensionHelper::DidFinishLoad(blink::WebLocalFrame* frame) {
179 SchedulerMap::iterator i = g_schedulers.Get().find(frame);
180 if (i != g_schedulers.Get().end())
181 i->second->DidFinishLoad();
182 }
183
DidCreateDocumentElement(WebLocalFrame * frame)184 void ExtensionHelper::DidCreateDocumentElement(WebLocalFrame* frame) {
185 dispatcher_->user_script_slave()->InjectScripts(
186 frame, UserScript::DOCUMENT_START);
187 SchedulerMap::iterator i = g_schedulers.Get().find(frame);
188 if (i != g_schedulers.Get().end())
189 i->second->DidCreateDocumentElement();
190
191 dispatcher_->DidCreateDocumentElement(frame);
192 }
193
DidStartProvisionalLoad(blink::WebLocalFrame * frame)194 void ExtensionHelper::DidStartProvisionalLoad(blink::WebLocalFrame* frame) {
195 SchedulerMap::iterator i = g_schedulers.Get().find(frame);
196 if (i != g_schedulers.Get().end())
197 i->second->DidStartProvisionalLoad();
198 }
199
DraggableRegionsChanged(blink::WebFrame * frame)200 void ExtensionHelper::DraggableRegionsChanged(blink::WebFrame* frame) {
201 blink::WebVector<blink::WebDraggableRegion> webregions =
202 frame->document().draggableRegions();
203 std::vector<DraggableRegion> regions;
204 for (size_t i = 0; i < webregions.size(); ++i) {
205 DraggableRegion region;
206 region.bounds = webregions[i].bounds;
207 region.draggable = webregions[i].draggable;
208 regions.push_back(region);
209 }
210 Send(new ExtensionHostMsg_UpdateDraggableRegions(routing_id(), regions));
211 }
212
FrameDetached(WebFrame * frame)213 void ExtensionHelper::FrameDetached(WebFrame* frame) {
214 // This could be called before DidCreateDataSource, in which case the frame
215 // won't be in the map.
216 SchedulerMap::iterator i = g_schedulers.Get().find(frame);
217 if (i == g_schedulers.Get().end())
218 return;
219
220 delete i->second;
221 g_schedulers.Get().erase(i);
222
223 dispatcher_->user_script_slave()->FrameDetached(frame);
224 }
225
DidMatchCSS(blink::WebLocalFrame * frame,const blink::WebVector<blink::WebString> & newly_matching_selectors,const blink::WebVector<blink::WebString> & stopped_matching_selectors)226 void ExtensionHelper::DidMatchCSS(
227 blink::WebLocalFrame* frame,
228 const blink::WebVector<blink::WebString>& newly_matching_selectors,
229 const blink::WebVector<blink::WebString>& stopped_matching_selectors) {
230 dispatcher_->DidMatchCSS(
231 frame, newly_matching_selectors, stopped_matching_selectors);
232 }
233
DidCreateDataSource(WebLocalFrame * frame,WebDataSource * ds)234 void ExtensionHelper::DidCreateDataSource(WebLocalFrame* frame,
235 WebDataSource* ds) {
236 // Check first if we created a scheduler for the frame, since this function
237 // gets called for navigations within the document.
238 if (g_schedulers.Get().count(frame))
239 return;
240
241 g_schedulers.Get()[frame] = new UserScriptScheduler(frame, dispatcher_);
242 }
243
OnExtensionResponse(int request_id,bool success,const base::ListValue & response,const std::string & error)244 void ExtensionHelper::OnExtensionResponse(int request_id,
245 bool success,
246 const base::ListValue& response,
247 const std::string& error) {
248 dispatcher_->OnExtensionResponse(request_id,
249 success,
250 response,
251 error);
252 }
253
OnExtensionMessageInvoke(const std::string & extension_id,const std::string & module_name,const std::string & function_name,const base::ListValue & args,bool user_gesture)254 void ExtensionHelper::OnExtensionMessageInvoke(const std::string& extension_id,
255 const std::string& module_name,
256 const std::string& function_name,
257 const base::ListValue& args,
258 bool user_gesture) {
259 dispatcher_->InvokeModuleSystemMethod(
260 render_view(), extension_id, module_name, function_name, args,
261 user_gesture);
262 }
263
OnExtensionDispatchOnConnect(int target_port_id,const std::string & channel_name,const base::DictionaryValue & source_tab,const ExtensionMsg_ExternalConnectionInfo & info,const std::string & tls_channel_id)264 void ExtensionHelper::OnExtensionDispatchOnConnect(
265 int target_port_id,
266 const std::string& channel_name,
267 const base::DictionaryValue& source_tab,
268 const ExtensionMsg_ExternalConnectionInfo& info,
269 const std::string& tls_channel_id) {
270 MessagingBindings::DispatchOnConnect(dispatcher_->script_context_set(),
271 target_port_id,
272 channel_name,
273 source_tab,
274 info,
275 tls_channel_id,
276 render_view());
277 }
278
OnExtensionDeliverMessage(int target_id,const Message & message)279 void ExtensionHelper::OnExtensionDeliverMessage(int target_id,
280 const Message& message) {
281 MessagingBindings::DeliverMessage(
282 dispatcher_->script_context_set(), target_id, message, render_view());
283 }
284
OnExtensionDispatchOnDisconnect(int port_id,const std::string & error_message)285 void ExtensionHelper::OnExtensionDispatchOnDisconnect(
286 int port_id,
287 const std::string& error_message) {
288 MessagingBindings::DispatchOnDisconnect(
289 dispatcher_->script_context_set(), port_id, error_message, render_view());
290 }
291
OnExecuteCode(const ExtensionMsg_ExecuteCode_Params & params)292 void ExtensionHelper::OnExecuteCode(
293 const ExtensionMsg_ExecuteCode_Params& params) {
294 WebView* webview = render_view()->GetWebView();
295 WebFrame* main_frame = webview->mainFrame();
296 if (!main_frame) {
297 base::ListValue val;
298 Send(new ExtensionHostMsg_ExecuteCodeFinished(routing_id(),
299 params.request_id,
300 "No main frame",
301 -1,
302 GURL(std::string()),
303 val));
304 return;
305 }
306
307 // chrome.tabs.executeScript() only supports execution in either the top frame
308 // or all frames. We handle both cases in the top frame.
309 SchedulerMap::iterator i = g_schedulers.Get().find(main_frame);
310 if (i != g_schedulers.Get().end())
311 i->second->ExecuteCode(params);
312 }
313
OnNotifyRendererViewType(ViewType type)314 void ExtensionHelper::OnNotifyRendererViewType(ViewType type) {
315 view_type_ = type;
316 }
317
OnSetTabId(int init_tab_id)318 void ExtensionHelper::OnSetTabId(int init_tab_id) {
319 CHECK_EQ(tab_id_, -1);
320 CHECK_GE(init_tab_id, 0);
321 tab_id_ = init_tab_id;
322 }
323
OnUpdateBrowserWindowId(int window_id)324 void ExtensionHelper::OnUpdateBrowserWindowId(int window_id) {
325 browser_window_id_ = window_id;
326 }
327
OnAddMessageToConsole(ConsoleMessageLevel level,const std::string & message)328 void ExtensionHelper::OnAddMessageToConsole(ConsoleMessageLevel level,
329 const std::string& message) {
330 console::AddMessage(render_view(), level, message);
331 }
332
OnAppWindowClosed()333 void ExtensionHelper::OnAppWindowClosed() {
334 v8::HandleScope scope(v8::Isolate::GetCurrent());
335 v8::Handle<v8::Context> v8_context =
336 render_view()->GetWebView()->mainFrame()->mainWorldScriptContext();
337 ScriptContext* script_context =
338 dispatcher_->script_context_set().GetByV8Context(v8_context);
339 if (!script_context)
340 return;
341 script_context->module_system()->CallModuleMethod("app.window",
342 "onAppWindowClosed");
343 }
344
OnGrantContentScriptPermission(int request_id)345 void ExtensionHelper::OnGrantContentScriptPermission(int request_id) {
346 dispatcher_->user_script_slave()->OnContentScriptGrantedPermission(
347 render_view(), request_id);
348 }
349
350 } // namespace extensions
351