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 "content/browser/browser_plugin/browser_plugin_guest.h" 6 7 #include <algorithm> 8 9 #include "base/command_line.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "content/browser/browser_plugin/browser_plugin_embedder.h" 14 #include "content/browser/browser_plugin/browser_plugin_guest_helper.h" 15 #include "content/browser/browser_plugin/browser_plugin_guest_manager.h" 16 #include "content/browser/browser_plugin/browser_plugin_host_factory.h" 17 #include "content/browser/browser_thread_impl.h" 18 #include "content/browser/loader/resource_dispatcher_host_impl.h" 19 #include "content/browser/renderer_host/render_view_host_impl.h" 20 #include "content/browser/renderer_host/render_widget_host_impl.h" 21 #include "content/browser/web_contents/web_contents_impl.h" 22 #include "content/browser/web_contents/web_contents_view_guest.h" 23 #include "content/common/browser_plugin/browser_plugin_constants.h" 24 #include "content/common/browser_plugin/browser_plugin_messages.h" 25 #include "content/common/content_constants_internal.h" 26 #include "content/common/drag_messages.h" 27 #include "content/common/gpu/gpu_messages.h" 28 #include "content/common/input_messages.h" 29 #include "content/common/view_messages.h" 30 #include "content/port/browser/render_view_host_delegate_view.h" 31 #include "content/public/browser/browser_context.h" 32 #include "content/public/browser/content_browser_client.h" 33 #include "content/public/browser/geolocation_permission_context.h" 34 #include "content/public/browser/notification_service.h" 35 #include "content/public/browser/notification_types.h" 36 #include "content/public/browser/render_process_host.h" 37 #include "content/public/browser/render_widget_host_view.h" 38 #include "content/public/browser/resource_request_details.h" 39 #include "content/public/browser/user_metrics.h" 40 #include "content/public/browser/web_contents_view.h" 41 #include "content/public/common/content_switches.h" 42 #include "content/public/common/drop_data.h" 43 #include "content/public/common/media_stream_request.h" 44 #include "content/public/common/result_codes.h" 45 #include "net/url_request/url_request.h" 46 #include "third_party/WebKit/public/web/WebCursorInfo.h" 47 #include "ui/base/keycodes/keyboard_codes.h" 48 #include "ui/surface/transport_dib.h" 49 #include "webkit/common/resource_type.h" 50 51 #if defined(OS_MACOSX) 52 #include "content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h" 53 #endif 54 55 namespace content { 56 57 // static 58 BrowserPluginHostFactory* BrowserPluginGuest::factory_ = NULL; 59 60 // Parent class for the various types of permission requests, each of which 61 // should be able to handle the response to their permission request. 62 class BrowserPluginGuest::PermissionRequest : 63 public base::RefCounted<BrowserPluginGuest::PermissionRequest> { 64 public: 65 virtual void Respond(bool should_allow, const std::string& user_input) = 0; 66 protected: 67 PermissionRequest() { 68 RecordAction(UserMetricsAction("BrowserPlugin.Guest.PermissionRequest")); 69 } 70 virtual ~PermissionRequest() {} 71 // Friend RefCounted so that the dtor can be non-public. 72 friend class base::RefCounted<BrowserPluginGuest::PermissionRequest>; 73 }; 74 75 class BrowserPluginGuest::DownloadRequest : public PermissionRequest { 76 public: 77 explicit DownloadRequest(base::Callback<void(bool)> callback) 78 : callback_(callback) { 79 RecordAction( 80 UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Download")); 81 } 82 virtual void Respond(bool should_allow, 83 const std::string& user_input) OVERRIDE { 84 callback_.Run(should_allow); 85 } 86 87 private: 88 virtual ~DownloadRequest() {} 89 base::Callback<void(bool)> callback_; 90 }; 91 92 class BrowserPluginGuest::GeolocationRequest : public PermissionRequest { 93 public: 94 GeolocationRequest(GeolocationCallback callback, 95 int bridge_id, 96 base::WeakPtrFactory<BrowserPluginGuest>* weak_ptr_factory) 97 : callback_(callback), 98 bridge_id_(bridge_id), 99 weak_ptr_factory_(weak_ptr_factory) { 100 RecordAction( 101 UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Geolocation")); 102 } 103 104 virtual void Respond(bool should_allow, 105 const std::string& user_input) OVERRIDE { 106 base::WeakPtr<BrowserPluginGuest> guest(weak_ptr_factory_->GetWeakPtr()); 107 108 WebContents* web_contents = guest->embedder_web_contents(); 109 if (should_allow && web_contents) { 110 // If renderer side embedder decides to allow gelocation, we need to check 111 // if the app/embedder itself has geolocation access. 112 BrowserContext* browser_context = web_contents->GetBrowserContext(); 113 if (browser_context) { 114 GeolocationPermissionContext* geolocation_context = 115 browser_context->GetGeolocationPermissionContext(); 116 if (geolocation_context) { 117 base::Callback<void(bool)> geolocation_callback = base::Bind( 118 &BrowserPluginGuest::SetGeolocationPermission, 119 guest, 120 callback_, 121 bridge_id_); 122 geolocation_context->RequestGeolocationPermission( 123 web_contents->GetRenderProcessHost()->GetID(), 124 web_contents->GetRoutingID(), 125 // The geolocation permission request here is not initiated 126 // through WebGeolocationPermissionRequest. We are only interested 127 // in the fact whether the embedder/app has geolocation 128 // permission. Therefore we use an invalid |bridge_id|. 129 -1 /* bridge_id */, 130 web_contents->GetURL(), 131 geolocation_callback); 132 return; 133 } 134 } 135 } 136 guest->SetGeolocationPermission(callback_, bridge_id_, false); 137 } 138 139 private: 140 virtual ~GeolocationRequest() {} 141 base::Callback<void(bool)> callback_; 142 int bridge_id_; 143 base::WeakPtrFactory<BrowserPluginGuest>* weak_ptr_factory_; 144 }; 145 146 class BrowserPluginGuest::MediaRequest : public PermissionRequest { 147 public: 148 MediaRequest(const MediaStreamRequest& request, 149 const MediaResponseCallback& callback, 150 BrowserPluginGuest* guest) 151 : request_(request), 152 callback_(callback), 153 guest_(guest) { 154 RecordAction( 155 UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Media")); 156 } 157 158 virtual void Respond(bool should_allow, 159 const std::string& user_input) OVERRIDE { 160 WebContentsImpl* web_contents = guest_->embedder_web_contents(); 161 if (should_allow && web_contents) { 162 // Re-route the request to the embedder's WebContents; the guest gets the 163 // permission this way. 164 web_contents->RequestMediaAccessPermission(request_, callback_); 165 } else { 166 // Deny the request. 167 callback_.Run(MediaStreamDevices(), scoped_ptr<MediaStreamUI>()); 168 } 169 } 170 171 private: 172 virtual ~MediaRequest() {} 173 MediaStreamRequest request_; 174 MediaResponseCallback callback_; 175 BrowserPluginGuest* guest_; 176 }; 177 178 class BrowserPluginGuest::NewWindowRequest : public PermissionRequest { 179 public: 180 NewWindowRequest(int instance_id, BrowserPluginGuest* guest) 181 : instance_id_(instance_id), 182 guest_(guest) { 183 RecordAction( 184 UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.NewWindow")); 185 } 186 187 virtual void Respond(bool should_allow, 188 const std::string& user_input) OVERRIDE { 189 int embedder_render_process_id = 190 guest_->embedder_web_contents()->GetRenderProcessHost()->GetID(); 191 BrowserPluginGuest* guest = 192 guest_->GetWebContents()->GetBrowserPluginGuestManager()-> 193 GetGuestByInstanceID(instance_id_, embedder_render_process_id); 194 if (!guest) { 195 LOG(INFO) << "Guest not found. Instance ID: " << instance_id_; 196 return; 197 } 198 199 // If we do not destroy the guest then we allow the new window. 200 if (!should_allow) 201 guest->Destroy(); 202 } 203 204 private: 205 virtual ~NewWindowRequest() {} 206 int instance_id_; 207 BrowserPluginGuest* guest_; 208 }; 209 210 class BrowserPluginGuest::JavaScriptDialogRequest : public PermissionRequest { 211 public: 212 JavaScriptDialogRequest(const DialogClosedCallback& callback) 213 : callback_(callback) { 214 RecordAction( 215 UserMetricsAction( 216 "BrowserPlugin.Guest.PermissionRequest.JavaScriptDialog")); 217 } 218 219 virtual void Respond(bool should_allow, 220 const std::string& user_input) OVERRIDE { 221 callback_.Run(should_allow, UTF8ToUTF16(user_input)); 222 } 223 224 private: 225 virtual ~JavaScriptDialogRequest() {} 226 DialogClosedCallback callback_; 227 }; 228 229 class BrowserPluginGuest::PointerLockRequest : public PermissionRequest { 230 public: 231 PointerLockRequest(BrowserPluginGuest* guest) 232 : guest_(guest) { 233 RecordAction( 234 UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.PointerLock")); 235 } 236 237 virtual void Respond(bool should_allow, 238 const std::string& user_input) OVERRIDE { 239 guest_->SendMessageToEmbedder( 240 new BrowserPluginMsg_SetMouseLock(guest_->instance_id(), should_allow)); 241 } 242 243 private: 244 virtual ~PointerLockRequest() {} 245 BrowserPluginGuest* guest_; 246 }; 247 248 namespace { 249 const size_t kNumMaxOutstandingPermissionRequests = 1024; 250 251 std::string WindowOpenDispositionToString( 252 WindowOpenDisposition window_open_disposition) { 253 switch (window_open_disposition) { 254 case IGNORE_ACTION: 255 return "ignore"; 256 case SAVE_TO_DISK: 257 return "save_to_disk"; 258 case CURRENT_TAB: 259 return "current_tab"; 260 case NEW_BACKGROUND_TAB: 261 return "new_background_tab"; 262 case NEW_FOREGROUND_TAB: 263 return "new_foreground_tab"; 264 case NEW_WINDOW: 265 return "new_window"; 266 case NEW_POPUP: 267 return "new_popup"; 268 default: 269 NOTREACHED() << "Unknown Window Open Disposition"; 270 return "ignore"; 271 } 272 } 273 274 std::string JavaScriptMessageTypeToString(JavaScriptMessageType message_type) { 275 switch (message_type) { 276 case JAVASCRIPT_MESSAGE_TYPE_ALERT: 277 return "alert"; 278 case JAVASCRIPT_MESSAGE_TYPE_CONFIRM: 279 return "confirm"; 280 case JAVASCRIPT_MESSAGE_TYPE_PROMPT: 281 return "prompt"; 282 default: 283 NOTREACHED() << "Unknown JavaScript Message Type."; 284 return "unknown"; 285 } 286 } 287 288 // Called on IO thread. 289 static std::string RetrieveDownloadURLFromRequestId( 290 RenderViewHost* render_view_host, 291 int url_request_id) { 292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 293 294 int render_process_id = render_view_host->GetProcess()->GetID(); 295 GlobalRequestID global_id(render_process_id, url_request_id); 296 net::URLRequest* url_request = 297 ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id); 298 if (url_request) 299 return url_request->url().possibly_invalid_spec(); 300 return std::string(); 301 } 302 303 } // namespace 304 305 class BrowserPluginGuest::EmbedderRenderViewHostObserver 306 : public RenderViewHostObserver { 307 public: 308 explicit EmbedderRenderViewHostObserver(BrowserPluginGuest* guest) 309 : RenderViewHostObserver( 310 guest->embedder_web_contents()->GetRenderViewHost()), 311 browser_plugin_guest_(guest) { 312 } 313 314 virtual ~EmbedderRenderViewHostObserver() { 315 } 316 317 // RenderViewHostObserver: 318 virtual void RenderViewHostDestroyed( 319 RenderViewHost* render_view_host) OVERRIDE { 320 browser_plugin_guest_->embedder_web_contents_ = NULL; 321 browser_plugin_guest_->Destroy(); 322 } 323 324 private: 325 BrowserPluginGuest* browser_plugin_guest_; 326 327 DISALLOW_COPY_AND_ASSIGN(EmbedderRenderViewHostObserver); 328 }; 329 330 BrowserPluginGuest::BrowserPluginGuest( 331 int instance_id, 332 WebContentsImpl* web_contents, 333 BrowserPluginGuest* opener, 334 bool has_render_view) 335 : WebContentsObserver(web_contents), 336 weak_ptr_factory_(this), 337 embedder_web_contents_(NULL), 338 instance_id_(instance_id), 339 damage_buffer_sequence_id_(0), 340 damage_buffer_size_(0), 341 damage_buffer_scale_factor_(1.0f), 342 guest_device_scale_factor_(1.0f), 343 guest_hang_timeout_( 344 base::TimeDelta::FromMilliseconds(kHungRendererDelayMs)), 345 focused_(false), 346 mouse_locked_(false), 347 pending_lock_request_(false), 348 embedder_visible_(true), 349 next_permission_request_id_(browser_plugin::kInvalidPermissionRequestID), 350 has_render_view_(has_render_view), 351 is_in_destruction_(false) { 352 DCHECK(web_contents); 353 web_contents->SetDelegate(this); 354 if (opener) 355 opener_ = opener->AsWeakPtr(); 356 GetWebContents()->GetBrowserPluginGuestManager()->AddGuest(instance_id_, 357 GetWebContents()); 358 } 359 360 bool BrowserPluginGuest::AddMessageToConsole(WebContents* source, 361 int32 level, 362 const string16& message, 363 int32 line_no, 364 const string16& source_id) { 365 if (!delegate_) 366 return false; 367 368 delegate_->AddMessageToConsole(level, message, line_no, source_id); 369 return true; 370 } 371 372 void BrowserPluginGuest::DestroyUnattachedWindows() { 373 // Destroy() reaches in and removes the BrowserPluginGuest from its opener's 374 // pending_new_windows_ set. To avoid mutating the set while iterating, we 375 // create a copy of the pending new windows set and iterate over the copy. 376 PendingWindowMap pending_new_windows(pending_new_windows_); 377 // Clean up unattached new windows opened by this guest. 378 for (PendingWindowMap::const_iterator it = pending_new_windows.begin(); 379 it != pending_new_windows.end(); ++it) { 380 it->first->Destroy(); 381 } 382 // All pending windows should be removed from the set after Destroy() is 383 // called on all of them. 384 DCHECK_EQ(0ul, pending_new_windows_.size()); 385 } 386 387 void BrowserPluginGuest::RespondToPermissionRequest( 388 int request_id, 389 bool should_allow, 390 const std::string& user_input) { 391 RequestMap::iterator request_itr = permission_request_map_.find(request_id); 392 if (request_itr == permission_request_map_.end()) { 393 LOG(INFO) << "Not a valid request ID."; 394 return; 395 } 396 request_itr->second->Respond(should_allow, user_input); 397 permission_request_map_.erase(request_itr); 398 } 399 400 int BrowserPluginGuest::RequestPermission( 401 BrowserPluginPermissionType permission_type, 402 scoped_refptr<BrowserPluginGuest::PermissionRequest> request, 403 const base::DictionaryValue& request_info) { 404 if (!delegate_) { 405 request->Respond(false, ""); 406 return browser_plugin::kInvalidPermissionRequestID; 407 } 408 409 int request_id = ++next_permission_request_id_; 410 permission_request_map_[request_id] = request; 411 412 BrowserPluginGuestDelegate::PermissionResponseCallback callback = 413 base::Bind(&BrowserPluginGuest::RespondToPermissionRequest, 414 AsWeakPtr(), 415 request_id); 416 // If BrowserPluginGuestDelegate hasn't handled the permission then we simply 417 // reject it immediately. 418 if (!delegate_->RequestPermission(permission_type, request_info, callback)) 419 callback.Run(false, ""); 420 421 return request_id; 422 } 423 424 void BrowserPluginGuest::Destroy() { 425 is_in_destruction_ = true; 426 if (!attached() && opener()) 427 opener()->pending_new_windows_.erase(this); 428 DestroyUnattachedWindows(); 429 GetWebContents()->GetBrowserPluginGuestManager()->RemoveGuest(instance_id_); 430 delete GetWebContents(); 431 } 432 433 bool BrowserPluginGuest::OnMessageReceivedFromEmbedder( 434 const IPC::Message& message) { 435 bool handled = true; 436 IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuest, message) 437 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK, 438 OnSwapBuffersACK) 439 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_CompositorFrameACK, 440 OnCompositorFrameACK) 441 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_DragStatusUpdate, 442 OnDragStatusUpdate) 443 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ExecuteEditCommand, 444 OnExecuteEditCommand) 445 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_HandleInputEvent, 446 OnHandleInputEvent) 447 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_LockMouse_ACK, OnLockMouseAck) 448 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_NavigateGuest, OnNavigateGuest) 449 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginDestroyed, OnPluginDestroyed) 450 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ResizeGuest, OnResizeGuest) 451 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetAutoSize, OnSetSize) 452 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent, 453 OnSetEditCommandsForNextKeyEvent) 454 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetFocus, OnSetFocus) 455 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetName, OnSetName) 456 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetVisibility, OnSetVisibility) 457 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UnlockMouse_ACK, OnUnlockMouseAck) 458 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UpdateGeometry, OnUpdateGeometry) 459 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UpdateRect_ACK, OnUpdateRectACK) 460 IPC_MESSAGE_UNHANDLED(handled = false) 461 IPC_END_MESSAGE_MAP() 462 return handled; 463 } 464 465 void BrowserPluginGuest::Initialize( 466 WebContentsImpl* embedder_web_contents, 467 const BrowserPluginHostMsg_Attach_Params& params) { 468 focused_ = params.focused; 469 guest_visible_ = params.visible; 470 guest_window_rect_ = params.resize_guest_params.view_rect; 471 472 if (!params.name.empty()) 473 name_ = params.name; 474 auto_size_enabled_ = params.auto_size_params.enable; 475 max_auto_size_ = params.auto_size_params.max_size; 476 min_auto_size_ = params.auto_size_params.min_size; 477 478 // Once a BrowserPluginGuest has an embedder WebContents, it's considered to 479 // be attached. 480 embedder_web_contents_ = embedder_web_contents; 481 482 WebContentsViewGuest* new_view = 483 static_cast<WebContentsViewGuest*>(GetWebContents()->GetView()); 484 new_view->OnGuestInitialized(embedder_web_contents->GetView()); 485 486 // |render_view_host| manages the ownership of this BrowserPluginGuestHelper. 487 new BrowserPluginGuestHelper(this, GetWebContents()->GetRenderViewHost()); 488 489 RendererPreferences* renderer_prefs = 490 GetWebContents()->GetMutableRendererPrefs(); 491 // Copy renderer preferences (and nothing else) from the embedder's 492 // WebContents to the guest. 493 // 494 // For GTK and Aura this is necessary to get proper renderer configuration 495 // values for caret blinking interval, colors related to selection and 496 // focus. 497 *renderer_prefs = *embedder_web_contents_->GetMutableRendererPrefs(); 498 499 // We would like the guest to report changes to frame names so that we can 500 // update the BrowserPlugin's corresponding 'name' attribute. 501 // TODO(fsamuel): Remove this once http://crbug.com/169110 is addressed. 502 renderer_prefs->report_frame_name_changes = true; 503 // Navigation is disabled in Chrome Apps. We want to make sure guest-initiated 504 // navigations still continue to function inside the app. 505 renderer_prefs->browser_handles_all_top_level_requests = false; 506 507 // Listen to embedder visibility changes so that the guest is in a 'shown' 508 // state if both the embedder is visible and the BrowserPlugin is marked as 509 // visible. 510 notification_registrar_.Add( 511 this, NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED, 512 Source<WebContents>(embedder_web_contents_)); 513 514 embedder_rvh_observer_.reset(new EmbedderRenderViewHostObserver(this)); 515 516 OnSetSize(instance_id_, params.auto_size_params, params.resize_guest_params); 517 518 // Create a swapped out RenderView for the guest in the embedder render 519 // process, so that the embedder can access the guest's window object. 520 int guest_routing_id = 521 GetWebContents()->CreateSwappedOutRenderView( 522 embedder_web_contents_->GetSiteInstance()); 523 SendMessageToEmbedder( 524 new BrowserPluginMsg_GuestContentWindowReady(instance_id_, 525 guest_routing_id)); 526 527 if (!params.src.empty()) 528 OnNavigateGuest(instance_id_, params.src); 529 530 has_render_view_ = true; 531 532 if (!embedder_web_contents_-> 533 GetWebkitPrefs().accelerated_compositing_enabled) { 534 WebPreferences prefs = GetWebContents()->GetWebkitPrefs(); 535 prefs.accelerated_compositing_enabled = false; 536 GetWebContents()->GetRenderViewHost()->UpdateWebkitPreferences(prefs); 537 } 538 539 // Enable input method for guest if it's enabled for the embedder. 540 if (static_cast<RenderViewHostImpl*>( 541 embedder_web_contents_->GetRenderViewHost())->input_method_active()) { 542 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( 543 GetWebContents()->GetRenderViewHost()); 544 guest_rvh->SetInputMethodActive(true); 545 } 546 } 547 548 BrowserPluginGuest::~BrowserPluginGuest() { 549 while (!pending_messages_.empty()) { 550 delete pending_messages_.front(); 551 pending_messages_.pop(); 552 } 553 } 554 555 // static 556 BrowserPluginGuest* BrowserPluginGuest::Create( 557 int instance_id, 558 WebContentsImpl* web_contents, 559 scoped_ptr<base::DictionaryValue> extra_params) { 560 RecordAction(UserMetricsAction("BrowserPlugin.Guest.Create")); 561 BrowserPluginGuest* guest = NULL; 562 if (factory_) { 563 guest = factory_->CreateBrowserPluginGuest(instance_id, web_contents); 564 } else { 565 guest = new BrowserPluginGuest(instance_id, web_contents, NULL, false); 566 } 567 web_contents->SetBrowserPluginGuest(guest); 568 BrowserPluginGuestDelegate* delegate = NULL; 569 GetContentClient()->browser()->GuestWebContentsCreated( 570 web_contents, NULL, &delegate, extra_params.Pass()); 571 guest->SetDelegate(delegate); 572 return guest; 573 } 574 575 // static 576 BrowserPluginGuest* BrowserPluginGuest::CreateWithOpener( 577 int instance_id, 578 WebContentsImpl* web_contents, 579 BrowserPluginGuest* opener, 580 bool has_render_view) { 581 BrowserPluginGuest* guest = 582 new BrowserPluginGuest( 583 instance_id, web_contents, opener, has_render_view); 584 web_contents->SetBrowserPluginGuest(guest); 585 BrowserPluginGuestDelegate* delegate = NULL; 586 GetContentClient()->browser()->GuestWebContentsCreated( 587 web_contents, opener->GetWebContents(), &delegate, 588 scoped_ptr<base::DictionaryValue>()); 589 guest->SetDelegate(delegate); 590 return guest; 591 } 592 593 RenderWidgetHostView* BrowserPluginGuest::GetEmbedderRenderWidgetHostView() { 594 return embedder_web_contents_->GetRenderWidgetHostView(); 595 } 596 597 void BrowserPluginGuest::UpdateVisibility() { 598 OnSetVisibility(instance_id_, visible()); 599 } 600 601 // screen. 602 gfx::Rect BrowserPluginGuest::ToGuestRect(const gfx::Rect& bounds) { 603 gfx::Rect guest_rect(bounds); 604 guest_rect.Offset(guest_window_rect_.OffsetFromOrigin()); 605 return guest_rect; 606 } 607 608 void BrowserPluginGuest::Observe(int type, 609 const NotificationSource& source, 610 const NotificationDetails& details) { 611 switch (type) { 612 case NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED: { 613 DCHECK_EQ(Source<WebContents>(source).ptr(), embedder_web_contents_); 614 embedder_visible_ = *Details<bool>(details).ptr(); 615 UpdateVisibility(); 616 break; 617 } 618 default: 619 NOTREACHED() << "Unexpected notification sent."; 620 break; 621 } 622 } 623 624 void BrowserPluginGuest::AddNewContents(WebContents* source, 625 WebContents* new_contents, 626 WindowOpenDisposition disposition, 627 const gfx::Rect& initial_pos, 628 bool user_gesture, 629 bool* was_blocked) { 630 if (was_blocked) 631 *was_blocked = false; 632 RequestNewWindowPermission(static_cast<WebContentsImpl*>(new_contents), 633 disposition, initial_pos, user_gesture); 634 } 635 636 void BrowserPluginGuest::CanDownload( 637 RenderViewHost* render_view_host, 638 int request_id, 639 const std::string& request_method, 640 const base::Callback<void(bool)>& callback) { 641 if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) { 642 // Deny the download request. 643 callback.Run(false); 644 return; 645 } 646 647 BrowserThread::PostTaskAndReplyWithResult( 648 BrowserThread::IO, FROM_HERE, 649 base::Bind(&RetrieveDownloadURLFromRequestId, 650 render_view_host, request_id), 651 base::Bind(&BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId, 652 weak_ptr_factory_.GetWeakPtr(), 653 request_method, 654 callback)); 655 } 656 657 void BrowserPluginGuest::CloseContents(WebContents* source) { 658 if (!delegate_) 659 return; 660 661 delegate_->Close(); 662 } 663 664 JavaScriptDialogManager* BrowserPluginGuest::GetJavaScriptDialogManager() { 665 return this; 666 } 667 668 bool BrowserPluginGuest::HandleContextMenu(const ContextMenuParams& params) { 669 // TODO(fsamuel): We show the regular page context menu handler for now until 670 // we implement the Apps Context Menu API for Browser Plugin (see 671 // http://crbug.com/140315). 672 return false; // Will be handled by WebContentsViewGuest. 673 } 674 675 void BrowserPluginGuest::HandleKeyboardEvent( 676 WebContents* source, 677 const NativeWebKeyboardEvent& event) { 678 if (!attached()) 679 return; 680 681 if (UnlockMouseIfNecessary(event)) 682 return; 683 684 if (delegate_ && delegate_->HandleKeyboardEvent(event)) 685 return; 686 687 // Send the unhandled keyboard events back to the embedder to reprocess them. 688 // TODO(fsamuel): This introduces the possibility of out-of-order keyboard 689 // events because the guest may be arbitrarily delayed when responding to 690 // keyboard events. In that time, the embedder may have received and processed 691 // additional key events. This needs to be fixed as soon as possible. 692 // See http://crbug.com/229882. 693 embedder_web_contents_->GetDelegate()->HandleKeyboardEvent( 694 web_contents(), event); 695 } 696 697 WebContents* BrowserPluginGuest::OpenURLFromTab(WebContents* source, 698 const OpenURLParams& params) { 699 // If the guest wishes to navigate away prior to attachment then we save the 700 // navigation to perform upon attachment. Navigation initializes a lot of 701 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest. 702 // Navigation also resumes resource loading which we don't want to allow 703 // until attachment. 704 if (!attached()) { 705 PendingWindowMap::iterator it = opener()->pending_new_windows_.find(this); 706 if (it == opener()->pending_new_windows_.end()) 707 return NULL; 708 const NewWindowInfo& old_target_url = it->second; 709 NewWindowInfo new_window_info(params.url, old_target_url.name); 710 new_window_info.changed = new_window_info.url != old_target_url.url; 711 it->second = new_window_info; 712 return NULL; 713 } 714 // This can happen for cross-site redirects. 715 source->GetController().LoadURL( 716 params.url, params.referrer, params.transition, std::string()); 717 return source; 718 } 719 720 void BrowserPluginGuest::WebContentsCreated(WebContents* source_contents, 721 int64 source_frame_id, 722 const string16& frame_name, 723 const GURL& target_url, 724 WebContents* new_contents) { 725 WebContentsImpl* new_contents_impl = 726 static_cast<WebContentsImpl*>(new_contents); 727 BrowserPluginGuest* guest = new_contents_impl->GetBrowserPluginGuest(); 728 guest->opener_ = AsWeakPtr(); 729 std::string guest_name = UTF16ToUTF8(frame_name); 730 guest->name_ = guest_name; 731 // Take ownership of the new guest until it is attached to the embedder's DOM 732 // tree to avoid leaking a guest if this guest is destroyed before attaching 733 // the new guest. 734 pending_new_windows_.insert( 735 std::make_pair(guest, NewWindowInfo(target_url, guest_name))); 736 } 737 738 void BrowserPluginGuest::RendererUnresponsive(WebContents* source) { 739 RecordAction(UserMetricsAction("BrowserPlugin.Guest.Hung")); 740 if (!delegate_) 741 return; 742 delegate_->RendererUnresponsive(); 743 } 744 745 void BrowserPluginGuest::RendererResponsive(WebContents* source) { 746 RecordAction(UserMetricsAction("BrowserPlugin.Guest.Responsive")); 747 if (!delegate_) 748 return; 749 delegate_->RendererResponsive(); 750 } 751 752 void BrowserPluginGuest::RunFileChooser(WebContents* web_contents, 753 const FileChooserParams& params) { 754 embedder_web_contents_->GetDelegate()->RunFileChooser(web_contents, params); 755 } 756 757 bool BrowserPluginGuest::ShouldFocusPageAfterCrash() { 758 // Rather than managing focus in WebContentsImpl::RenderViewReady, we will 759 // manage the focus ourselves. 760 return false; 761 } 762 763 WebContentsImpl* BrowserPluginGuest::GetWebContents() { 764 return static_cast<WebContentsImpl*>(web_contents()); 765 } 766 767 base::SharedMemory* BrowserPluginGuest::GetDamageBufferFromEmbedder( 768 const BrowserPluginHostMsg_ResizeGuest_Params& params) { 769 #if defined(OS_WIN) 770 base::ProcessHandle handle = 771 embedder_web_contents_->GetRenderProcessHost()->GetHandle(); 772 scoped_ptr<base::SharedMemory> shared_buf( 773 new base::SharedMemory(params.damage_buffer_handle, false, handle)); 774 #elif defined(OS_POSIX) 775 scoped_ptr<base::SharedMemory> shared_buf( 776 new base::SharedMemory(params.damage_buffer_handle, false)); 777 #endif 778 if (!shared_buf->Map(params.damage_buffer_size)) { 779 LOG(WARNING) << "Unable to map the embedder's damage buffer."; 780 return NULL; 781 } 782 return shared_buf.release(); 783 } 784 785 void BrowserPluginGuest::SetDamageBuffer( 786 const BrowserPluginHostMsg_ResizeGuest_Params& params) { 787 damage_buffer_.reset(GetDamageBufferFromEmbedder(params)); 788 // Sanity check: Verify that we've correctly shared the damage buffer memory 789 // between the embedder and browser processes. 790 DCHECK(!damage_buffer_ || 791 *static_cast<unsigned int*>(damage_buffer_->memory()) == 0xdeadbeef); 792 damage_buffer_sequence_id_ = params.damage_buffer_sequence_id; 793 damage_buffer_size_ = params.damage_buffer_size; 794 damage_view_size_ = params.view_rect.size(); 795 damage_buffer_scale_factor_ = params.scale_factor; 796 } 797 798 gfx::Point BrowserPluginGuest::GetScreenCoordinates( 799 const gfx::Point& relative_position) const { 800 gfx::Point screen_pos(relative_position); 801 screen_pos += guest_window_rect_.OffsetFromOrigin(); 802 return screen_pos; 803 } 804 805 bool BrowserPluginGuest::InAutoSizeBounds(const gfx::Size& size) const { 806 return size.width() <= max_auto_size_.width() && 807 size.height() <= max_auto_size_.height(); 808 } 809 810 void BrowserPluginGuest::RequestNewWindowPermission( 811 WebContentsImpl* new_contents, 812 WindowOpenDisposition disposition, 813 const gfx::Rect& initial_bounds, 814 bool user_gesture) { 815 BrowserPluginGuest* guest = new_contents->GetBrowserPluginGuest(); 816 PendingWindowMap::iterator it = pending_new_windows_.find(guest); 817 if (it == pending_new_windows_.end()) 818 return; 819 const NewWindowInfo& new_window_info = it->second; 820 821 base::DictionaryValue request_info; 822 request_info.Set(browser_plugin::kInitialHeight, 823 base::Value::CreateIntegerValue(initial_bounds.height())); 824 request_info.Set(browser_plugin::kInitialWidth, 825 base::Value::CreateIntegerValue(initial_bounds.width())); 826 request_info.Set(browser_plugin::kTargetURL, 827 base::Value::CreateStringValue(new_window_info.url.spec())); 828 request_info.Set(browser_plugin::kName, 829 base::Value::CreateStringValue(new_window_info.name)); 830 request_info.Set(browser_plugin::kWindowID, 831 base::Value::CreateIntegerValue(guest->instance_id())); 832 request_info.Set(browser_plugin::kWindowOpenDisposition, 833 base::Value::CreateStringValue( 834 WindowOpenDispositionToString(disposition))); 835 836 RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW, 837 new NewWindowRequest(guest->instance_id(), this), 838 request_info); 839 } 840 841 bool BrowserPluginGuest::UnlockMouseIfNecessary( 842 const NativeWebKeyboardEvent& event) { 843 if (!mouse_locked_) 844 return false; 845 846 embedder_web_contents()->GotResponseToLockMouseRequest(false); 847 return true; 848 } 849 850 void BrowserPluginGuest::SendMessageToEmbedder(IPC::Message* msg) { 851 if (!attached()) { 852 // Some pages such as data URLs, javascript URLs, and about:blank 853 // do not load external resources and so they load prior to attachment. 854 // As a result, we must save all these IPCs until attachment and then 855 // forward them so that the embedder gets a chance to see and process 856 // the load events. 857 pending_messages_.push(msg); 858 return; 859 } 860 msg->set_routing_id(embedder_web_contents_->GetRoutingID()); 861 embedder_web_contents_->Send(msg); 862 } 863 864 void BrowserPluginGuest::DragSourceEndedAt(int client_x, int client_y, 865 int screen_x, int screen_y, WebKit::WebDragOperation operation) { 866 web_contents()->GetRenderViewHost()->DragSourceEndedAt(client_x, client_y, 867 screen_x, screen_y, operation); 868 } 869 870 void BrowserPluginGuest::DragSourceMovedTo(int client_x, int client_y, 871 int screen_x, int screen_y) { 872 web_contents()->GetRenderViewHost()->DragSourceMovedTo(client_x, client_y, 873 screen_x, screen_y); 874 } 875 876 void BrowserPluginGuest::EndSystemDrag() { 877 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( 878 GetWebContents()->GetRenderViewHost()); 879 guest_rvh->DragSourceSystemDragEnded(); 880 // Issue a MouseUp event to get out of a selection state. 881 WebKit::WebMouseEvent mouse_event; 882 mouse_event.type = WebKit::WebInputEvent::MouseUp; 883 mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; 884 guest_rvh->ForwardMouseEvent(mouse_event); 885 } 886 887 void BrowserPluginGuest::SetDelegate(BrowserPluginGuestDelegate* delegate) { 888 DCHECK(!delegate_); 889 delegate_.reset(delegate); 890 } 891 892 void BrowserPluginGuest::AskEmbedderForGeolocationPermission( 893 int bridge_id, 894 const GURL& requesting_frame, 895 const GeolocationCallback& callback) { 896 if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) { 897 // Deny the geolocation request. 898 callback.Run(false); 899 return; 900 } 901 902 base::DictionaryValue request_info; 903 request_info.Set(browser_plugin::kURL, 904 base::Value::CreateStringValue(requesting_frame.spec())); 905 906 int request_id = 907 RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION, 908 new GeolocationRequest( 909 callback, bridge_id, &weak_ptr_factory_), 910 request_info); 911 912 DCHECK(bridge_id_to_request_id_map_.find(bridge_id) == 913 bridge_id_to_request_id_map_.end()); 914 bridge_id_to_request_id_map_[bridge_id] = request_id; 915 } 916 917 int BrowserPluginGuest::RemoveBridgeID(int bridge_id) { 918 std::map<int, int>::iterator bridge_itr = 919 bridge_id_to_request_id_map_.find(bridge_id); 920 if (bridge_itr == bridge_id_to_request_id_map_.end()) 921 return browser_plugin::kInvalidPermissionRequestID; 922 923 int request_id = bridge_itr->second; 924 bridge_id_to_request_id_map_.erase(bridge_itr); 925 return request_id; 926 } 927 928 void BrowserPluginGuest::CancelGeolocationRequest(int bridge_id) { 929 int request_id = RemoveBridgeID(bridge_id); 930 RequestMap::iterator request_itr = permission_request_map_.find(request_id); 931 if (request_itr == permission_request_map_.end()) 932 return; 933 permission_request_map_.erase(request_itr); 934 } 935 936 void BrowserPluginGuest::SetGeolocationPermission(GeolocationCallback callback, 937 int bridge_id, 938 bool allowed) { 939 callback.Run(allowed); 940 RemoveBridgeID(bridge_id); 941 } 942 943 void BrowserPluginGuest::SendQueuedMessages() { 944 if (!attached()) 945 return; 946 947 while (!pending_messages_.empty()) { 948 IPC::Message* message = pending_messages_.front(); 949 pending_messages_.pop(); 950 SendMessageToEmbedder(message); 951 } 952 } 953 954 void BrowserPluginGuest::DidCommitProvisionalLoadForFrame( 955 int64 frame_id, 956 bool is_main_frame, 957 const GURL& url, 958 PageTransition transition_type, 959 RenderViewHost* render_view_host) { 960 RecordAction(UserMetricsAction("BrowserPlugin.Guest.DidNavigate")); 961 } 962 963 void BrowserPluginGuest::DidStopLoading(RenderViewHost* render_view_host) { 964 bool disable_dragdrop = !CommandLine::ForCurrentProcess()->HasSwitch( 965 switches::kEnableBrowserPluginDragDrop); 966 if (disable_dragdrop) { 967 // Initiating a drag from inside a guest is currently not supported without 968 // the kEnableBrowserPluginDragDrop flag on a linux platform. So inject some 969 // JS to disable it. http://crbug.com/161112 970 const char script[] = "window.addEventListener('dragstart', function() { " 971 " window.event.preventDefault(); " 972 "});"; 973 render_view_host->ExecuteJavascriptInWebFrame(string16(), 974 ASCIIToUTF16(script)); 975 } 976 } 977 978 void BrowserPluginGuest::RenderViewReady() { 979 // TODO(fsamuel): Investigate whether it's possible to update state earlier 980 // here (see http://crbug.com/158151). 981 Send(new InputMsg_SetFocus(routing_id(), focused_)); 982 UpdateVisibility(); 983 RenderViewHost* rvh = GetWebContents()->GetRenderViewHost(); 984 if (auto_size_enabled_) 985 rvh->EnableAutoResize(min_auto_size_, max_auto_size_); 986 else 987 rvh->DisableAutoResize(damage_view_size_); 988 989 Send(new ViewMsg_SetName(routing_id(), name_)); 990 991 RenderWidgetHostImpl::From(rvh)-> 992 set_hung_renderer_delay_ms(guest_hang_timeout_); 993 } 994 995 void BrowserPluginGuest::RenderProcessGone(base::TerminationStatus status) { 996 SendMessageToEmbedder(new BrowserPluginMsg_GuestGone(instance_id())); 997 switch (status) { 998 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: 999 RecordAction(UserMetricsAction("BrowserPlugin.Guest.Killed")); 1000 break; 1001 case base::TERMINATION_STATUS_PROCESS_CRASHED: 1002 RecordAction(UserMetricsAction("BrowserPlugin.Guest.Crashed")); 1003 break; 1004 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: 1005 RecordAction(UserMetricsAction("BrowserPlugin.Guest.AbnormalDeath")); 1006 break; 1007 default: 1008 break; 1009 } 1010 // TODO(fsamuel): Consider whether we should be clearing 1011 // |permission_request_map_| here. 1012 if (delegate_) 1013 delegate_->GuestProcessGone(status); 1014 } 1015 1016 // static 1017 void BrowserPluginGuest::AcknowledgeBufferPresent( 1018 int route_id, 1019 int gpu_host_id, 1020 const std::string& mailbox_name, 1021 uint32 sync_point) { 1022 AcceleratedSurfaceMsg_BufferPresented_Params ack_params; 1023 ack_params.mailbox_name = mailbox_name; 1024 ack_params.sync_point = sync_point; 1025 RenderWidgetHostImpl::AcknowledgeBufferPresent(route_id, 1026 gpu_host_id, 1027 ack_params); 1028 } 1029 1030 // static 1031 bool BrowserPluginGuest::ShouldForwardToBrowserPluginGuest( 1032 const IPC::Message& message) { 1033 switch (message.type()) { 1034 case BrowserPluginHostMsg_BuffersSwappedACK::ID: 1035 case BrowserPluginHostMsg_CompositorFrameACK::ID: 1036 case BrowserPluginHostMsg_DragStatusUpdate::ID: 1037 case BrowserPluginHostMsg_ExecuteEditCommand::ID: 1038 case BrowserPluginHostMsg_HandleInputEvent::ID: 1039 case BrowserPluginHostMsg_LockMouse_ACK::ID: 1040 case BrowserPluginHostMsg_NavigateGuest::ID: 1041 case BrowserPluginHostMsg_PluginDestroyed::ID: 1042 case BrowserPluginHostMsg_ResizeGuest::ID: 1043 case BrowserPluginHostMsg_SetAutoSize::ID: 1044 case BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent::ID: 1045 case BrowserPluginHostMsg_SetFocus::ID: 1046 case BrowserPluginHostMsg_SetName::ID: 1047 case BrowserPluginHostMsg_SetVisibility::ID: 1048 case BrowserPluginHostMsg_UnlockMouse_ACK::ID: 1049 case BrowserPluginHostMsg_UpdateGeometry::ID: 1050 case BrowserPluginHostMsg_UpdateRect_ACK::ID: 1051 return true; 1052 default: 1053 break; 1054 } 1055 return false; 1056 } 1057 1058 bool BrowserPluginGuest::OnMessageReceived(const IPC::Message& message) { 1059 bool handled = true; 1060 IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuest, message) 1061 IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, 1062 OnHasTouchEventHandlers) 1063 IPC_MESSAGE_HANDLER(ViewHostMsg_LockMouse, OnLockMouse) 1064 IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnSetCursor) 1065 #if defined(OS_MACOSX) 1066 // MacOSX creates and populates platform-specific select drop-down menus 1067 // whereas other platforms merely create a popup window that the guest 1068 // renderer process paints inside. 1069 IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnShowPopup) 1070 #endif 1071 IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnShowWidget) 1072 IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus) 1073 IPC_MESSAGE_HANDLER(ViewHostMsg_UnlockMouse, OnUnlockMouse) 1074 IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFrameName, OnUpdateFrameName) 1075 IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect) 1076 IPC_MESSAGE_UNHANDLED(handled = false) 1077 IPC_END_MESSAGE_MAP() 1078 return handled; 1079 } 1080 1081 void BrowserPluginGuest::Attach( 1082 WebContentsImpl* embedder_web_contents, 1083 BrowserPluginHostMsg_Attach_Params params) { 1084 if (attached()) 1085 return; 1086 1087 // Clear parameters that get inherited from the opener. 1088 params.storage_partition_id.clear(); 1089 params.persist_storage = false; 1090 params.src.clear(); 1091 1092 // If a RenderView has already been created for this new window, then we need 1093 // to initialize the browser-side state now so that the RenderViewHostManager 1094 // does not create a new RenderView on navigation. 1095 if (has_render_view_) { 1096 static_cast<RenderViewHostImpl*>( 1097 GetWebContents()->GetRenderViewHost())->Init(); 1098 WebContentsViewGuest* new_view = 1099 static_cast<WebContentsViewGuest*>(GetWebContents()->GetView()); 1100 new_view->CreateViewForWidget(web_contents()->GetRenderViewHost()); 1101 } 1102 1103 // We need to do a navigation here if the target URL has changed between 1104 // the time the WebContents was created and the time it was attached. 1105 // We also need to do an initial navigation if a RenderView was never 1106 // created for the new window in cases where there is no referrer. 1107 PendingWindowMap::iterator it = opener()->pending_new_windows_.find(this); 1108 if (it != opener()->pending_new_windows_.end()) { 1109 const NewWindowInfo& new_window_info = it->second; 1110 if (new_window_info.changed || !has_render_view_) 1111 params.src = it->second.url.spec(); 1112 } else { 1113 NOTREACHED(); 1114 } 1115 1116 // Once a new guest is attached to the DOM of the embedder page, then the 1117 // lifetime of the new guest is no longer managed by the opener guest. 1118 opener()->pending_new_windows_.erase(this); 1119 1120 // The guest's frame name takes precedence over the BrowserPlugin's name. 1121 // The guest's frame name is assigned in 1122 // BrowserPluginGuest::WebContentsCreated. 1123 if (!name_.empty()) 1124 params.name.clear(); 1125 1126 Initialize(embedder_web_contents, params); 1127 1128 // Inform the embedder of the guest's information. 1129 // We pull the partition information from the site's URL, which is of the form 1130 // guest://site/{persist}?{partition_name}. 1131 const GURL& site_url = GetWebContents()->GetSiteInstance()->GetSiteURL(); 1132 BrowserPluginMsg_Attach_ACK_Params ack_params; 1133 ack_params.storage_partition_id = site_url.query(); 1134 ack_params.persist_storage = 1135 site_url.path().find("persist") != std::string::npos; 1136 ack_params.name = name_; 1137 SendMessageToEmbedder( 1138 new BrowserPluginMsg_Attach_ACK(instance_id_, ack_params)); 1139 1140 SendQueuedMessages(); 1141 1142 RecordAction(UserMetricsAction("BrowserPlugin.Guest.Attached")); 1143 } 1144 1145 void BrowserPluginGuest::OnCompositorFrameACK( 1146 int instance_id, 1147 int route_id, 1148 uint32 output_surface_id, 1149 int renderer_host_id, 1150 const cc::CompositorFrameAck& ack) { 1151 RenderWidgetHostImpl::SendSwapCompositorFrameAck(route_id, 1152 output_surface_id, 1153 renderer_host_id, 1154 ack); 1155 } 1156 1157 void BrowserPluginGuest::OnDragStatusUpdate(int instance_id, 1158 WebKit::WebDragStatus drag_status, 1159 const DropData& drop_data, 1160 WebKit::WebDragOperationsMask mask, 1161 const gfx::Point& location) { 1162 RenderViewHost* host = GetWebContents()->GetRenderViewHost(); 1163 switch (drag_status) { 1164 case WebKit::WebDragStatusEnter: 1165 embedder_web_contents_->GetBrowserPluginEmbedder()->DragEnteredGuest( 1166 this); 1167 host->DragTargetDragEnter(drop_data, location, location, mask, 0); 1168 break; 1169 case WebKit::WebDragStatusOver: 1170 host->DragTargetDragOver(location, location, mask, 0); 1171 break; 1172 case WebKit::WebDragStatusLeave: 1173 embedder_web_contents_->GetBrowserPluginEmbedder()->DragLeftGuest(this); 1174 host->DragTargetDragLeave(); 1175 break; 1176 case WebKit::WebDragStatusDrop: 1177 host->DragTargetDrop(location, location, 0); 1178 EndSystemDrag(); 1179 break; 1180 case WebKit::WebDragStatusUnknown: 1181 NOTREACHED(); 1182 } 1183 } 1184 1185 void BrowserPluginGuest::OnExecuteEditCommand(int instance_id, 1186 const std::string& name) { 1187 Send(new InputMsg_ExecuteEditCommand(routing_id(), name, std::string())); 1188 } 1189 1190 void BrowserPluginGuest::OnHandleInputEvent( 1191 int instance_id, 1192 const gfx::Rect& guest_window_rect, 1193 const WebKit::WebInputEvent* event) { 1194 guest_window_rect_ = guest_window_rect; 1195 // If the embedder's RWHV is destroyed then that means that the embedder's 1196 // window has been closed but the embedder's WebContents has not yet been 1197 // destroyed. Computing screen coordinates of a BrowserPlugin only makes sense 1198 // if there is a visible embedder. 1199 if (embedder_web_contents_->GetRenderWidgetHostView()) { 1200 guest_screen_rect_ = guest_window_rect; 1201 guest_screen_rect_.Offset( 1202 embedder_web_contents_->GetRenderWidgetHostView()-> 1203 GetViewBounds().OffsetFromOrigin()); 1204 } 1205 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( 1206 GetWebContents()->GetRenderViewHost()); 1207 1208 if (WebKit::WebInputEvent::isMouseEventType(event->type)) { 1209 guest_rvh->ForwardMouseEvent( 1210 *static_cast<const WebKit::WebMouseEvent*>(event)); 1211 return; 1212 } 1213 1214 if (event->type == WebKit::WebInputEvent::MouseWheel) { 1215 guest_rvh->ForwardWheelEvent( 1216 *static_cast<const WebKit::WebMouseWheelEvent*>(event)); 1217 return; 1218 } 1219 1220 if (WebKit::WebInputEvent::isKeyboardEventType(event->type)) { 1221 RenderViewHostImpl* embedder_rvh = static_cast<RenderViewHostImpl*>( 1222 embedder_web_contents_->GetRenderViewHost()); 1223 if (!embedder_rvh->GetLastKeyboardEvent()) 1224 return; 1225 NativeWebKeyboardEvent keyboard_event( 1226 *embedder_rvh->GetLastKeyboardEvent()); 1227 guest_rvh->ForwardKeyboardEvent(keyboard_event); 1228 return; 1229 } 1230 1231 if (WebKit::WebInputEvent::isTouchEventType(event->type)) { 1232 guest_rvh->ForwardTouchEventWithLatencyInfo( 1233 *static_cast<const WebKit::WebTouchEvent*>(event), 1234 ui::LatencyInfo()); 1235 return; 1236 } 1237 1238 if (WebKit::WebInputEvent::isGestureEventType(event->type)) { 1239 guest_rvh->ForwardGestureEvent( 1240 *static_cast<const WebKit::WebGestureEvent*>(event)); 1241 return; 1242 } 1243 } 1244 1245 void BrowserPluginGuest::OnLockMouse(bool user_gesture, 1246 bool last_unlocked_by_target, 1247 bool privileged) { 1248 if (pending_lock_request_ || 1249 (permission_request_map_.size() >= 1250 kNumMaxOutstandingPermissionRequests)) { 1251 // Immediately reject the lock because only one pointerLock may be active 1252 // at a time. 1253 Send(new ViewMsg_LockMouse_ACK(routing_id(), false)); 1254 return; 1255 } 1256 pending_lock_request_ = true; 1257 base::DictionaryValue request_info; 1258 request_info.Set(browser_plugin::kUserGesture, 1259 base::Value::CreateBooleanValue(user_gesture)); 1260 request_info.Set(browser_plugin::kLastUnlockedBySelf, 1261 base::Value::CreateBooleanValue(last_unlocked_by_target)); 1262 request_info.Set(browser_plugin::kURL, 1263 base::Value::CreateStringValue( 1264 web_contents()->GetURL().spec())); 1265 1266 RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK, 1267 new PointerLockRequest(this), 1268 request_info); 1269 } 1270 1271 void BrowserPluginGuest::OnLockMouseAck(int instance_id, bool succeeded) { 1272 Send(new ViewMsg_LockMouse_ACK(routing_id(), succeeded)); 1273 pending_lock_request_ = false; 1274 if (succeeded) 1275 mouse_locked_ = true; 1276 } 1277 1278 void BrowserPluginGuest::OnNavigateGuest( 1279 int instance_id, 1280 const std::string& src) { 1281 GURL url(src); 1282 // We do not load empty urls in web_contents. 1283 // If a guest sets empty src attribute after it has navigated to some 1284 // non-empty page, the action is considered no-op. This empty src navigation 1285 // should never be sent to BrowserPluginGuest (browser process). 1286 DCHECK(!src.empty()); 1287 if (!src.empty()) { 1288 // As guests do not swap processes on navigation, only navigations to 1289 // normal web URLs are supported. No protocol handlers are installed for 1290 // other schemes (e.g., WebUI or extensions), and no permissions or bindings 1291 // can be granted to the guest process. 1292 GetWebContents()->GetController().LoadURL(url, Referrer(), 1293 PAGE_TRANSITION_AUTO_TOPLEVEL, 1294 std::string()); 1295 } 1296 } 1297 1298 void BrowserPluginGuest::OnPluginDestroyed(int instance_id) { 1299 Destroy(); 1300 } 1301 1302 void BrowserPluginGuest::OnResizeGuest( 1303 int instance_id, 1304 const BrowserPluginHostMsg_ResizeGuest_Params& params) { 1305 if (!params.size_changed) 1306 return; 1307 // BrowserPlugin manages resize flow control itself and does not depend 1308 // on RenderWidgetHost's mechanisms for flow control, so we reset those flags 1309 // here. If we are setting the size for the first time before navigating then 1310 // BrowserPluginGuest does not yet have a RenderViewHost. 1311 if (GetWebContents()->GetRenderViewHost()) { 1312 RenderWidgetHostImpl* render_widget_host = 1313 RenderWidgetHostImpl::From(GetWebContents()->GetRenderViewHost()); 1314 render_widget_host->ResetSizeAndRepaintPendingFlags(); 1315 1316 if (guest_device_scale_factor_ != params.scale_factor) { 1317 guest_device_scale_factor_ = params.scale_factor; 1318 render_widget_host->NotifyScreenInfoChanged(); 1319 } 1320 } 1321 // Invalid damage buffer means we are in HW compositing mode, 1322 // so just resize the WebContents and repaint if needed. 1323 if (!base::SharedMemory::IsHandleValid(params.damage_buffer_handle)) { 1324 if (!params.view_rect.size().IsEmpty()) 1325 GetWebContents()->GetView()->SizeContents(params.view_rect.size()); 1326 if (params.repaint) 1327 Send(new ViewMsg_Repaint(routing_id(), params.view_rect.size())); 1328 return; 1329 } 1330 SetDamageBuffer(params); 1331 GetWebContents()->GetView()->SizeContents(params.view_rect.size()); 1332 if (params.repaint) 1333 Send(new ViewMsg_Repaint(routing_id(), params.view_rect.size())); 1334 } 1335 1336 void BrowserPluginGuest::OnSetFocus(int instance_id, bool focused) { 1337 if (focused_ == focused) 1338 return; 1339 focused_ = focused; 1340 Send(new InputMsg_SetFocus(routing_id(), focused)); 1341 if (!focused && mouse_locked_) 1342 OnUnlockMouse(); 1343 } 1344 1345 void BrowserPluginGuest::OnSetName(int instance_id, const std::string& name) { 1346 if (name == name_) 1347 return; 1348 name_ = name; 1349 Send(new ViewMsg_SetName(routing_id(), name)); 1350 } 1351 1352 void BrowserPluginGuest::OnSetSize( 1353 int instance_id, 1354 const BrowserPluginHostMsg_AutoSize_Params& auto_size_params, 1355 const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params) { 1356 bool old_auto_size_enabled = auto_size_enabled_; 1357 gfx::Size old_max_size = max_auto_size_; 1358 gfx::Size old_min_size = min_auto_size_; 1359 auto_size_enabled_ = auto_size_params.enable; 1360 max_auto_size_ = auto_size_params.max_size; 1361 min_auto_size_ = auto_size_params.min_size; 1362 if (auto_size_enabled_ && (!old_auto_size_enabled || 1363 (old_max_size != max_auto_size_) || 1364 (old_min_size != min_auto_size_))) { 1365 GetWebContents()->GetRenderViewHost()->EnableAutoResize( 1366 min_auto_size_, max_auto_size_); 1367 // TODO(fsamuel): If we're changing autosize parameters, then we force 1368 // the guest to completely repaint itself, because BrowserPlugin has 1369 // allocated a new damage buffer and expects a full frame of pixels. 1370 // Ideally, we shouldn't need to do this because we shouldn't need to 1371 // allocate a new damage buffer unless |max_auto_size_| has changed. 1372 // However, even in that case, layout may not change and so we may 1373 // not get a full frame worth of pixels. 1374 Send(new ViewMsg_Repaint(routing_id(), max_auto_size_)); 1375 } else if (!auto_size_enabled_ && old_auto_size_enabled) { 1376 GetWebContents()->GetRenderViewHost()->DisableAutoResize( 1377 resize_guest_params.view_rect.size()); 1378 } 1379 OnResizeGuest(instance_id_, resize_guest_params); 1380 } 1381 1382 void BrowserPluginGuest::OnSetEditCommandsForNextKeyEvent( 1383 int instance_id, 1384 const std::vector<EditCommand>& edit_commands) { 1385 Send(new InputMsg_SetEditCommandsForNextKeyEvent(routing_id(), 1386 edit_commands)); 1387 } 1388 1389 void BrowserPluginGuest::OnSetVisibility(int instance_id, bool visible) { 1390 guest_visible_ = visible; 1391 if (embedder_visible_ && guest_visible_) 1392 GetWebContents()->WasShown(); 1393 else 1394 GetWebContents()->WasHidden(); 1395 } 1396 1397 void BrowserPluginGuest::OnSwapBuffersACK(int instance_id, 1398 int route_id, 1399 int gpu_host_id, 1400 const std::string& mailbox_name, 1401 uint32 sync_point) { 1402 AcknowledgeBufferPresent(route_id, gpu_host_id, mailbox_name, sync_point); 1403 1404 // This is only relevant on MACOSX and WIN when threaded compositing 1405 // is not enabled. In threaded mode, above ACK is sufficient. 1406 #if defined(OS_MACOSX) || defined(OS_WIN) 1407 RenderWidgetHostImpl* render_widget_host = 1408 RenderWidgetHostImpl::From(GetWebContents()->GetRenderViewHost()); 1409 render_widget_host->AcknowledgeSwapBuffersToRenderer(); 1410 #endif // defined(OS_MACOSX) || defined(OS_WIN) 1411 } 1412 1413 void BrowserPluginGuest::OnUnlockMouse() { 1414 SendMessageToEmbedder( 1415 new BrowserPluginMsg_SetMouseLock(instance_id(), false)); 1416 } 1417 1418 void BrowserPluginGuest::OnUnlockMouseAck(int instance_id) { 1419 // mouse_locked_ could be false here if the lock attempt was cancelled due 1420 // to window focus, or for various other reasons before the guest was informed 1421 // of the lock's success. 1422 if (mouse_locked_) 1423 Send(new ViewMsg_MouseLockLost(routing_id())); 1424 mouse_locked_ = false; 1425 } 1426 1427 void BrowserPluginGuest::OnUpdateRectACK( 1428 int instance_id, 1429 bool needs_ack, 1430 const BrowserPluginHostMsg_AutoSize_Params& auto_size_params, 1431 const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params) { 1432 // Only the software path expects an ACK. 1433 if (needs_ack) 1434 Send(new ViewMsg_UpdateRect_ACK(routing_id())); 1435 OnSetSize(instance_id_, auto_size_params, resize_guest_params); 1436 } 1437 1438 void BrowserPluginGuest::OnUpdateGeometry(int instance_id, 1439 const gfx::Rect& view_rect) { 1440 // The plugin has moved within the embedder without resizing or the 1441 // embedder/container's view rect changing. 1442 guest_window_rect_ = view_rect; 1443 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 1444 GetWebContents()->GetRenderViewHost()); 1445 if (rvh) 1446 rvh->SendScreenRects(); 1447 } 1448 1449 void BrowserPluginGuest::OnHasTouchEventHandlers(bool accept) { 1450 SendMessageToEmbedder( 1451 new BrowserPluginMsg_ShouldAcceptTouchEvents(instance_id(), accept)); 1452 } 1453 1454 void BrowserPluginGuest::OnSetCursor(const WebCursor& cursor) { 1455 SendMessageToEmbedder(new BrowserPluginMsg_SetCursor(instance_id(), cursor)); 1456 } 1457 1458 #if defined(OS_MACOSX) 1459 void BrowserPluginGuest::OnShowPopup( 1460 const ViewHostMsg_ShowPopup_Params& params) { 1461 gfx::Rect translated_bounds(params.bounds); 1462 translated_bounds.Offset(guest_window_rect_.OffsetFromOrigin()); 1463 BrowserPluginPopupMenuHelper popup_menu_helper( 1464 embedder_web_contents_->GetRenderViewHost(), 1465 GetWebContents()->GetRenderViewHost()); 1466 popup_menu_helper.ShowPopupMenu(translated_bounds, 1467 params.item_height, 1468 params.item_font_size, 1469 params.selected_item, 1470 params.popup_items, 1471 params.right_aligned, 1472 params.allow_multiple_selection); 1473 } 1474 #endif 1475 1476 void BrowserPluginGuest::OnShowWidget(int route_id, 1477 const gfx::Rect& initial_pos) { 1478 GetWebContents()->ShowCreatedWidget(route_id, initial_pos); 1479 } 1480 1481 void BrowserPluginGuest::OnTakeFocus(bool reverse) { 1482 SendMessageToEmbedder( 1483 new BrowserPluginMsg_AdvanceFocus(instance_id(), reverse)); 1484 } 1485 1486 void BrowserPluginGuest::OnUpdateFrameName(int frame_id, 1487 bool is_top_level, 1488 const std::string& name) { 1489 if (!is_top_level) 1490 return; 1491 1492 name_ = name; 1493 SendMessageToEmbedder(new BrowserPluginMsg_UpdatedName(instance_id_, name)); 1494 } 1495 1496 void BrowserPluginGuest::RequestMediaAccessPermission( 1497 WebContents* web_contents, 1498 const MediaStreamRequest& request, 1499 const MediaResponseCallback& callback) { 1500 if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) { 1501 // Deny the media request. 1502 callback.Run(MediaStreamDevices(), scoped_ptr<MediaStreamUI>()); 1503 return; 1504 } 1505 1506 base::DictionaryValue request_info; 1507 request_info.Set( 1508 browser_plugin::kURL, 1509 base::Value::CreateStringValue(request.security_origin.spec())); 1510 1511 RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA, 1512 new MediaRequest(request, callback, this), 1513 request_info); 1514 } 1515 1516 void BrowserPluginGuest::RunJavaScriptDialog( 1517 WebContents* web_contents, 1518 const GURL& origin_url, 1519 const std::string& accept_lang, 1520 JavaScriptMessageType javascript_message_type, 1521 const string16& message_text, 1522 const string16& default_prompt_text, 1523 const DialogClosedCallback& callback, 1524 bool* did_suppress_message) { 1525 if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) { 1526 // Cancel the dialog. 1527 callback.Run(false, string16()); 1528 return; 1529 } 1530 base::DictionaryValue request_info; 1531 request_info.Set( 1532 browser_plugin::kDefaultPromptText, 1533 base::Value::CreateStringValue(UTF16ToUTF8(default_prompt_text))); 1534 request_info.Set( 1535 browser_plugin::kMessageText, 1536 base::Value::CreateStringValue(UTF16ToUTF8(message_text))); 1537 request_info.Set( 1538 browser_plugin::kMessageType, 1539 base::Value::CreateStringValue( 1540 JavaScriptMessageTypeToString(javascript_message_type))); 1541 request_info.Set( 1542 browser_plugin::kURL, 1543 base::Value::CreateStringValue(origin_url.spec())); 1544 1545 RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG, 1546 new JavaScriptDialogRequest(callback), 1547 request_info); 1548 } 1549 1550 void BrowserPluginGuest::RunBeforeUnloadDialog( 1551 WebContents* web_contents, 1552 const string16& message_text, 1553 bool is_reload, 1554 const DialogClosedCallback& callback) { 1555 // This is called if the guest has a beforeunload event handler. 1556 // This callback allows navigation to proceed. 1557 callback.Run(true, string16()); 1558 } 1559 1560 bool BrowserPluginGuest::HandleJavaScriptDialog( 1561 WebContents* web_contents, 1562 bool accept, 1563 const string16* prompt_override) { 1564 return false; 1565 } 1566 1567 void BrowserPluginGuest::CancelActiveAndPendingDialogs( 1568 WebContents* web_contents) { 1569 } 1570 1571 void BrowserPluginGuest::WebContentsDestroyed(WebContents* web_contents) { 1572 } 1573 1574 void BrowserPluginGuest::OnUpdateRect( 1575 const ViewHostMsg_UpdateRect_Params& params) { 1576 BrowserPluginMsg_UpdateRect_Params relay_params; 1577 relay_params.view_size = params.view_size; 1578 relay_params.scale_factor = params.scale_factor; 1579 relay_params.is_resize_ack = ViewHostMsg_UpdateRect_Flags::is_resize_ack( 1580 params.flags); 1581 relay_params.needs_ack = params.needs_ack; 1582 1583 // HW accelerated case, acknowledge resize only 1584 if (!params.needs_ack || !damage_buffer_) { 1585 relay_params.damage_buffer_sequence_id = 0; 1586 SendMessageToEmbedder( 1587 new BrowserPluginMsg_UpdateRect(instance_id(), relay_params)); 1588 return; 1589 } 1590 1591 // Only copy damage if the guest is in autosize mode and the guest's view size 1592 // is less than the maximum size or the guest's view size is equal to the 1593 // damage buffer's size and the guest's scale factor is equal to the damage 1594 // buffer's scale factor. 1595 // The scaling change can happen due to asynchronous updates of the DPI on a 1596 // resolution change. 1597 if (((auto_size_enabled_ && InAutoSizeBounds(params.view_size)) || 1598 (params.view_size == damage_view_size())) && 1599 params.scale_factor == damage_buffer_scale_factor()) { 1600 TransportDIB* dib = GetWebContents()->GetRenderProcessHost()-> 1601 GetTransportDIB(params.bitmap); 1602 if (dib) { 1603 size_t guest_damage_buffer_size = 1604 #if defined(OS_WIN) 1605 params.bitmap_rect.width() * 1606 params.bitmap_rect.height() * 4; 1607 #else 1608 dib->size(); 1609 #endif 1610 size_t embedder_damage_buffer_size = damage_buffer_size_; 1611 void* guest_memory = dib->memory(); 1612 void* embedder_memory = damage_buffer_->memory(); 1613 size_t size = std::min(guest_damage_buffer_size, 1614 embedder_damage_buffer_size); 1615 memcpy(embedder_memory, guest_memory, size); 1616 } 1617 } 1618 relay_params.damage_buffer_sequence_id = damage_buffer_sequence_id_; 1619 relay_params.bitmap_rect = params.bitmap_rect; 1620 relay_params.scroll_delta = params.scroll_delta; 1621 relay_params.scroll_rect = params.scroll_rect; 1622 relay_params.copy_rects = params.copy_rects; 1623 1624 SendMessageToEmbedder( 1625 new BrowserPluginMsg_UpdateRect(instance_id(), relay_params)); 1626 } 1627 1628 void BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId( 1629 const std::string& request_method, 1630 const base::Callback<void(bool)>& callback, 1631 const std::string& url) { 1632 if (url.empty()) { 1633 callback.Run(false); 1634 return; 1635 } 1636 1637 base::DictionaryValue request_info; 1638 request_info.Set(browser_plugin::kRequestMethod, 1639 base::Value::CreateStringValue(request_method)); 1640 request_info.Set(browser_plugin::kURL, base::Value::CreateStringValue(url)); 1641 1642 RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD, 1643 new DownloadRequest(callback), 1644 request_info); 1645 } 1646 1647 } // namespace content 1648