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