• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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