1 // Copyright (c) 2012 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 "chrome/renderer/extensions/extension_helper.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/command_line.h" 10 #include "base/json/json_string_value_serializer.h" 11 #include "base/lazy_instance.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/common/chrome_switches.h" 15 #include "chrome/common/extensions/api/messaging/message.h" 16 #include "chrome/common/extensions/extension_constants.h" 17 #include "chrome/common/extensions/extension_messages.h" 18 #include "chrome/common/render_messages.h" 19 #include "chrome/common/url_constants.h" 20 #include "chrome/renderer/extensions/chrome_v8_context.h" 21 #include "chrome/renderer/extensions/console.h" 22 #include "chrome/renderer/extensions/dispatcher.h" 23 #include "chrome/renderer/extensions/messaging_bindings.h" 24 #include "chrome/renderer/extensions/user_script_scheduler.h" 25 #include "chrome/renderer/extensions/user_script_slave.h" 26 #include "chrome/renderer/web_apps.h" 27 #include "content/public/renderer/render_view.h" 28 #include "content/public/renderer/render_view_visitor.h" 29 #include "extensions/common/constants.h" 30 #include "third_party/WebKit/public/platform/WebURLRequest.h" 31 #include "third_party/WebKit/public/web/WebConsoleMessage.h" 32 #include "third_party/WebKit/public/web/WebDocument.h" 33 #include "third_party/WebKit/public/web/WebFrame.h" 34 #include "third_party/WebKit/public/web/WebScopedUserGesture.h" 35 #include "third_party/WebKit/public/web/WebView.h" 36 37 using content::ConsoleMessageLevel; 38 using blink::WebConsoleMessage; 39 using blink::WebDataSource; 40 using blink::WebFrame; 41 using blink::WebURLRequest; 42 using blink::WebScopedUserGesture; 43 using blink::WebView; 44 45 namespace extensions { 46 47 namespace { 48 // Keeps a mapping from the frame pointer to a UserScriptScheduler object. 49 // We store this mapping per process, because a frame can jump from one 50 // document to another with adoptNode, and so having the object be a 51 // RenderViewObserver means it might miss some notifications after it moves. 52 typedef std::map<WebFrame*, UserScriptScheduler*> SchedulerMap; 53 static base::LazyInstance<SchedulerMap> g_schedulers = 54 LAZY_INSTANCE_INITIALIZER; 55 56 // A RenderViewVisitor class that iterates through the set of available 57 // views, looking for a view of the given type, in the given browser window 58 // and within the given extension. 59 // Used to accumulate the list of views associated with an extension. 60 class ViewAccumulator : public content::RenderViewVisitor { 61 public: 62 ViewAccumulator(const std::string& extension_id, 63 int browser_window_id, 64 ViewType view_type) 65 : extension_id_(extension_id), 66 browser_window_id_(browser_window_id), 67 view_type_(view_type) { 68 } 69 70 std::vector<content::RenderView*> views() { return views_; } 71 72 // Returns false to terminate the iteration. 73 virtual bool Visit(content::RenderView* render_view) OVERRIDE { 74 ExtensionHelper* helper = ExtensionHelper::Get(render_view); 75 if (!ViewTypeMatches(helper->view_type(), view_type_)) 76 return true; 77 78 GURL url = render_view->GetWebView()->mainFrame()->document().url(); 79 if (!url.SchemeIs(kExtensionScheme)) 80 return true; 81 const std::string& extension_id = url.host(); 82 if (extension_id != extension_id_) 83 return true; 84 85 if (browser_window_id_ != extension_misc::kUnknownWindowId && 86 helper->browser_window_id() != browser_window_id_) { 87 return true; 88 } 89 90 views_.push_back(render_view); 91 92 if (view_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) 93 return false; // There can be only one... 94 return true; 95 } 96 97 private: 98 // Returns true if |type| "isa" |match|. 99 static bool ViewTypeMatches(ViewType type, ViewType match) { 100 if (type == match) 101 return true; 102 103 // INVALID means match all. 104 if (match == VIEW_TYPE_INVALID) 105 return true; 106 107 return false; 108 } 109 110 std::string extension_id_; 111 int browser_window_id_; 112 ViewType view_type_; 113 std::vector<content::RenderView*> views_; 114 }; 115 116 } // namespace 117 118 // static 119 std::vector<content::RenderView*> ExtensionHelper::GetExtensionViews( 120 const std::string& extension_id, 121 int browser_window_id, 122 ViewType view_type) { 123 ViewAccumulator accumulator(extension_id, browser_window_id, view_type); 124 content::RenderView::ForEach(&accumulator); 125 return accumulator.views(); 126 } 127 128 // static 129 content::RenderView* ExtensionHelper::GetBackgroundPage( 130 const std::string& extension_id) { 131 ViewAccumulator accumulator(extension_id, extension_misc::kUnknownWindowId, 132 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); 133 content::RenderView::ForEach(&accumulator); 134 CHECK_LE(accumulator.views().size(), 1u); 135 if (accumulator.views().size() == 0) 136 return NULL; 137 return accumulator.views()[0]; 138 } 139 140 ExtensionHelper::ExtensionHelper(content::RenderView* render_view, 141 Dispatcher* dispatcher) 142 : content::RenderViewObserver(render_view), 143 content::RenderViewObserverTracker<ExtensionHelper>(render_view), 144 dispatcher_(dispatcher), 145 pending_app_icon_requests_(0), 146 view_type_(VIEW_TYPE_INVALID), 147 tab_id_(-1), 148 browser_window_id_(-1) { 149 } 150 151 ExtensionHelper::~ExtensionHelper() { 152 } 153 154 bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) { 155 bool handled = true; 156 IPC_BEGIN_MESSAGE_MAP(ExtensionHelper, message) 157 IPC_MESSAGE_HANDLER(ExtensionMsg_Response, OnExtensionResponse) 158 IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnExtensionMessageInvoke) 159 IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect, 160 OnExtensionDispatchOnConnect) 161 IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnExtensionDeliverMessage) 162 IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect, 163 OnExtensionDispatchOnDisconnect) 164 IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode) 165 IPC_MESSAGE_HANDLER(ExtensionMsg_GetApplicationInfo, OnGetApplicationInfo) 166 IPC_MESSAGE_HANDLER(ExtensionMsg_SetTabId, OnSetTabId) 167 IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateBrowserWindowId, 168 OnUpdateBrowserWindowId) 169 IPC_MESSAGE_HANDLER(ExtensionMsg_NotifyRenderViewType, 170 OnNotifyRendererViewType) 171 IPC_MESSAGE_HANDLER(ExtensionMsg_AddMessageToConsole, 172 OnAddMessageToConsole) 173 IPC_MESSAGE_HANDLER(ExtensionMsg_AppWindowClosed, 174 OnAppWindowClosed); 175 IPC_MESSAGE_UNHANDLED(handled = false) 176 IPC_END_MESSAGE_MAP() 177 return handled; 178 } 179 180 void ExtensionHelper::DidFinishDocumentLoad(WebFrame* frame) { 181 dispatcher_->user_script_slave()->InjectScripts( 182 frame, UserScript::DOCUMENT_END); 183 184 SchedulerMap::iterator i = g_schedulers.Get().find(frame); 185 if (i != g_schedulers.Get().end()) 186 i->second->DidFinishDocumentLoad(); 187 } 188 189 void ExtensionHelper::DidFinishLoad(blink::WebFrame* frame) { 190 SchedulerMap::iterator i = g_schedulers.Get().find(frame); 191 if (i != g_schedulers.Get().end()) 192 i->second->DidFinishLoad(); 193 } 194 195 void ExtensionHelper::DidCreateDocumentElement(WebFrame* frame) { 196 dispatcher_->user_script_slave()->InjectScripts( 197 frame, UserScript::DOCUMENT_START); 198 SchedulerMap::iterator i = g_schedulers.Get().find(frame); 199 if (i != g_schedulers.Get().end()) 200 i->second->DidCreateDocumentElement(); 201 202 dispatcher_->DidCreateDocumentElement(frame); 203 } 204 205 void ExtensionHelper::DidStartProvisionalLoad(blink::WebFrame* frame) { 206 SchedulerMap::iterator i = g_schedulers.Get().find(frame); 207 if (i != g_schedulers.Get().end()) 208 i->second->DidStartProvisionalLoad(); 209 } 210 211 void ExtensionHelper::DraggableRegionsChanged(blink::WebFrame* frame) { 212 blink::WebVector<blink::WebDraggableRegion> webregions = 213 frame->document().draggableRegions(); 214 std::vector<DraggableRegion> regions; 215 for (size_t i = 0; i < webregions.size(); ++i) { 216 DraggableRegion region; 217 region.bounds = webregions[i].bounds; 218 region.draggable = webregions[i].draggable; 219 regions.push_back(region); 220 } 221 Send(new ExtensionHostMsg_UpdateDraggableRegions(routing_id(), regions)); 222 } 223 224 void ExtensionHelper::FrameDetached(WebFrame* frame) { 225 // This could be called before DidCreateDataSource, in which case the frame 226 // won't be in the map. 227 SchedulerMap::iterator i = g_schedulers.Get().find(frame); 228 if (i == g_schedulers.Get().end()) 229 return; 230 231 delete i->second; 232 g_schedulers.Get().erase(i); 233 } 234 235 void ExtensionHelper::DidMatchCSS( 236 blink::WebFrame* frame, 237 const blink::WebVector<blink::WebString>& newly_matching_selectors, 238 const blink::WebVector<blink::WebString>& stopped_matching_selectors) { 239 dispatcher_->DidMatchCSS( 240 frame, newly_matching_selectors, stopped_matching_selectors); 241 } 242 243 void ExtensionHelper::DidCreateDataSource(WebFrame* frame, WebDataSource* ds) { 244 // Check first if we created a scheduler for the frame, since this function 245 // gets called for navigations within the document. 246 if (g_schedulers.Get().count(frame)) 247 return; 248 249 g_schedulers.Get()[frame] = new UserScriptScheduler(frame, dispatcher_); 250 } 251 252 void ExtensionHelper::OnExtensionResponse(int request_id, 253 bool success, 254 const base::ListValue& response, 255 const std::string& error) { 256 dispatcher_->OnExtensionResponse(request_id, 257 success, 258 response, 259 error); 260 } 261 262 void ExtensionHelper::OnExtensionMessageInvoke(const std::string& extension_id, 263 const std::string& module_name, 264 const std::string& function_name, 265 const base::ListValue& args, 266 bool user_gesture) { 267 dispatcher_->InvokeModuleSystemMethod( 268 render_view(), extension_id, module_name, function_name, args, 269 user_gesture); 270 } 271 272 void ExtensionHelper::OnExtensionDispatchOnConnect( 273 int target_port_id, 274 const std::string& channel_name, 275 const base::DictionaryValue& source_tab, 276 const ExtensionMsg_ExternalConnectionInfo& info, 277 const std::string& tls_channel_id) { 278 MessagingBindings::DispatchOnConnect( 279 dispatcher_->v8_context_set().GetAll(), 280 target_port_id, channel_name, source_tab, 281 info.source_id, info.target_id, info.source_url, 282 tls_channel_id, render_view()); 283 } 284 285 void ExtensionHelper::OnExtensionDeliverMessage(int target_id, 286 const Message& message) { 287 MessagingBindings::DeliverMessage(dispatcher_->v8_context_set().GetAll(), 288 target_id, 289 message, 290 render_view()); 291 } 292 293 void ExtensionHelper::OnExtensionDispatchOnDisconnect( 294 int port_id, 295 const std::string& error_message) { 296 MessagingBindings::DispatchOnDisconnect( 297 dispatcher_->v8_context_set().GetAll(), 298 port_id, error_message, 299 render_view()); 300 } 301 302 void ExtensionHelper::OnExecuteCode( 303 const ExtensionMsg_ExecuteCode_Params& params) { 304 WebView* webview = render_view()->GetWebView(); 305 WebFrame* main_frame = webview->mainFrame(); 306 if (!main_frame) { 307 ListValue val; 308 Send(new ExtensionHostMsg_ExecuteCodeFinished(routing_id(), 309 params.request_id, 310 "No main frame", 311 -1, 312 GURL(std::string()), 313 val)); 314 return; 315 } 316 317 // chrome.tabs.executeScript() only supports execution in either the top frame 318 // or all frames. We handle both cases in the top frame. 319 SchedulerMap::iterator i = g_schedulers.Get().find(main_frame); 320 if (i != g_schedulers.Get().end()) 321 i->second->ExecuteCode(params); 322 } 323 324 void ExtensionHelper::OnGetApplicationInfo(int page_id) { 325 WebApplicationInfo app_info; 326 if (page_id == render_view()->GetPageId()) { 327 base::string16 error; 328 web_apps::ParseWebAppFromWebDocument( 329 render_view()->GetWebView()->mainFrame(), &app_info, &error); 330 } 331 332 // Prune out any data URLs in the set of icons. The browser process expects 333 // any icon with a data URL to have originated from a favicon. We don't want 334 // to decode arbitrary data URLs in the browser process. See 335 // http://b/issue?id=1162972 336 for (size_t i = 0; i < app_info.icons.size(); ++i) { 337 if (app_info.icons[i].url.SchemeIs(chrome::kDataScheme)) { 338 app_info.icons.erase(app_info.icons.begin() + i); 339 --i; 340 } 341 } 342 343 Send(new ExtensionHostMsg_DidGetApplicationInfo( 344 routing_id(), page_id, app_info)); 345 } 346 347 void ExtensionHelper::OnNotifyRendererViewType(ViewType type) { 348 view_type_ = type; 349 } 350 351 void ExtensionHelper::OnSetTabId(int init_tab_id) { 352 CHECK_EQ(tab_id_, -1); 353 CHECK_GE(init_tab_id, 0); 354 tab_id_ = init_tab_id; 355 } 356 357 void ExtensionHelper::OnUpdateBrowserWindowId(int window_id) { 358 browser_window_id_ = window_id; 359 } 360 361 void ExtensionHelper::OnAddMessageToConsole(ConsoleMessageLevel level, 362 const std::string& message) { 363 console::AddMessage(render_view(), level, message); 364 } 365 366 void ExtensionHelper::OnAppWindowClosed() { 367 v8::HandleScope scope(v8::Isolate::GetCurrent()); 368 v8::Handle<v8::Context> script_context = 369 render_view()->GetWebView()->mainFrame()->mainWorldScriptContext(); 370 ChromeV8Context* chrome_v8_context = 371 dispatcher_->v8_context_set().GetByV8Context(script_context); 372 if (!chrome_v8_context) 373 return; 374 chrome_v8_context->module_system()->CallModuleMethod( 375 "app.window", "onAppWindowClosed"); 376 } 377 378 } // namespace extensions 379