browser_plugin.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/renderer/browser_plugin/browser_plugin.h" 6 7#include "base/command_line.h" 8#include "base/json/json_string_value_serializer.h" 9#include "base/message_loop.h" 10#include "base/string_number_conversions.h" 11#include "base/string_util.h" 12#include "base/utf_string_conversions.h" 13#include "content/common/browser_plugin/browser_plugin_constants.h" 14#include "content/common/browser_plugin/browser_plugin_messages.h" 15#include "content/common/view_messages.h" 16#include "content/public/common/content_client.h" 17#include "content/public/common/content_switches.h" 18#include "content/public/renderer/content_renderer_client.h" 19#include "content/renderer/browser_plugin/browser_plugin_bindings.h" 20#include "content/renderer/browser_plugin/browser_plugin_compositing_helper.h" 21#include "content/renderer/browser_plugin/browser_plugin_manager.h" 22#include "content/renderer/render_process_impl.h" 23#include "content/renderer/render_thread_impl.h" 24#include "content/renderer/v8_value_converter_impl.h" 25#include "skia/ext/platform_canvas.h" 26#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h" 27#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" 28#include "third_party/WebKit/Source/WebKit/chromium/public/WebDOMCustomEvent.h" 29#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" 30#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" 31#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 32#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" 33#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" 34#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginParams.h" 35#include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h" 36#include "ui/base/keycodes/keyboard_codes.h" 37#include "webkit/plugins/sad_plugin.h" 38 39#if defined (OS_WIN) 40#include "base/sys_info.h" 41#endif 42 43using WebKit::WebCanvas; 44using WebKit::WebPluginContainer; 45using WebKit::WebPluginParams; 46using WebKit::WebPoint; 47using WebKit::WebRect; 48using WebKit::WebURL; 49using WebKit::WebVector; 50 51namespace content { 52 53namespace { 54 55static std::string TerminationStatusToString(base::TerminationStatus status) { 56 switch (status) { 57 case base::TERMINATION_STATUS_NORMAL_TERMINATION: 58 return "normal"; 59 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: 60 case base::TERMINATION_STATUS_STILL_RUNNING: 61 return "abnormal"; 62 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: 63 return "killed"; 64 case base::TERMINATION_STATUS_PROCESS_CRASHED: 65 return "crashed"; 66 case base::TERMINATION_STATUS_MAX_ENUM: 67 break; 68 } 69 NOTREACHED() << "Unknown Termination Status."; 70 return "unknown"; 71} 72 73static std::string GetInternalEventName(const char* event_name) { 74 return base::StringPrintf("-internal-%s", event_name); 75} 76 77static std::string PermissionTypeToString(BrowserPluginPermissionType type) { 78 switch (type) { 79 case BrowserPluginPermissionTypeDownload: 80 return browser_plugin::kPermissionTypeDownload; 81 case BrowserPluginPermissionTypeGeolocation: 82 return browser_plugin::kPermissionTypeGeolocation; 83 case BrowserPluginPermissionTypeMedia: 84 return browser_plugin::kPermissionTypeMedia; 85 case BrowserPluginPermissionTypeNewWindow: 86 return browser_plugin::kPermissionTypeNewWindow; 87 case BrowserPluginPermissionTypePointerLock: 88 return browser_plugin::kPermissionTypePointerLock; 89 case BrowserPluginPermissionTypeUnknown: 90 default: 91 NOTREACHED(); 92 break; 93 } 94 return std::string(); 95} 96 97typedef std::map<WebKit::WebPluginContainer*, 98 BrowserPlugin*> PluginContainerMap; 99static base::LazyInstance<PluginContainerMap> g_plugin_container_map = 100 LAZY_INSTANCE_INITIALIZER; 101 102} // namespace 103 104BrowserPlugin::BrowserPlugin( 105 RenderViewImpl* render_view, 106 WebKit::WebFrame* frame, 107 const WebPluginParams& params) 108 : instance_id_(browser_plugin::kInstanceIDNone), 109 render_view_(render_view->AsWeakPtr()), 110 render_view_routing_id_(render_view->GetRoutingID()), 111 container_(NULL), 112 damage_buffer_sequence_id_(0), 113 resize_ack_received_(true), 114 last_device_scale_factor_(1.0f), 115 sad_guest_(NULL), 116 guest_crashed_(false), 117 auto_size_ack_pending_(false), 118 guest_process_id_(-1), 119 guest_route_id_(-1), 120 persist_storage_(false), 121 valid_partition_id_(true), 122 content_window_routing_id_(MSG_ROUTING_NONE), 123 plugin_focused_(false), 124 visible_(true), 125 size_changed_in_flight_(false), 126 before_first_navigation_(true), 127 browser_plugin_manager_(render_view->GetBrowserPluginManager()), 128 current_nav_entry_index_(0), 129 nav_entry_count_(0), 130 compositing_enabled_(false), 131 weak_ptr_factory_(this) { 132} 133 134BrowserPlugin::~BrowserPlugin() { 135 // If the BrowserPlugin has never navigated then the browser process and 136 // BrowserPluginManager don't know about it and so there is nothing to do 137 // here. 138 if (!HasGuest()) 139 return; 140 browser_plugin_manager()->RemoveBrowserPlugin(instance_id_); 141 browser_plugin_manager()->Send( 142 new BrowserPluginHostMsg_PluginDestroyed(render_view_routing_id_, 143 instance_id_)); 144} 145 146/*static*/ 147BrowserPlugin* BrowserPlugin::FromContainer( 148 WebKit::WebPluginContainer* container) { 149 PluginContainerMap* browser_plugins = g_plugin_container_map.Pointer(); 150 PluginContainerMap::iterator it = browser_plugins->find(container); 151 return it == browser_plugins->end() ? NULL : it->second; 152} 153 154bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) { 155 bool handled = true; 156 IPC_BEGIN_MESSAGE_MAP(BrowserPlugin, message) 157 IPC_MESSAGE_HANDLER(BrowserPluginMsg_AddMessageToConsole, 158 OnAddMessageToConsole) 159 IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus) 160 IPC_MESSAGE_HANDLER(BrowserPluginMsg_Attach_ACK, OnAttachACK) 161 IPC_MESSAGE_HANDLER(BrowserPluginMsg_BuffersSwapped, OnBuffersSwapped) 162 IPC_MESSAGE_HANDLER(BrowserPluginMsg_Close, OnClose) 163 IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped, 164 OnCompositorFrameSwapped(message)) 165 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestContentWindowReady, 166 OnGuestContentWindowReady) 167 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone, OnGuestGone) 168 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestResponsive, OnGuestResponsive) 169 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestUnresponsive, OnGuestUnresponsive) 170 IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadAbort, OnLoadAbort) 171 IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadCommit, OnLoadCommit) 172 IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadRedirect, OnLoadRedirect) 173 IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadStart, OnLoadStart) 174 IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadStop, OnLoadStop) 175 IPC_MESSAGE_HANDLER(BrowserPluginMsg_RequestPermission, OnRequestPermission) 176 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor) 177 IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents, 178 OnShouldAcceptTouchEvents) 179 IPC_MESSAGE_HANDLER(BrowserPluginMsg_UnlockMouse, OnUnlockMouse) 180 IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdatedName, OnUpdatedName) 181 IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdateRect, OnUpdateRect) 182 IPC_MESSAGE_UNHANDLED(handled = false) 183 IPC_END_MESSAGE_MAP() 184 return handled; 185} 186 187void BrowserPlugin::UpdateDOMAttribute(const std::string& attribute_name, 188 const std::string& attribute_value) { 189 if (!container()) 190 return; 191 192 WebKit::WebElement element = container()->element(); 193 WebKit::WebString web_attribute_name = 194 WebKit::WebString::fromUTF8(attribute_name); 195 if (!HasDOMAttribute(attribute_name) || 196 (std::string(element.getAttribute(web_attribute_name).utf8()) != 197 attribute_value)) { 198 element.setAttribute(web_attribute_name, 199 WebKit::WebString::fromUTF8(attribute_value)); 200 } 201} 202 203void BrowserPlugin::RemoveDOMAttribute(const std::string& attribute_name) { 204 if (!container()) 205 return; 206 207 container()->element().removeAttribute( 208 WebKit::WebString::fromUTF8(attribute_name)); 209} 210 211std::string BrowserPlugin::GetDOMAttributeValue( 212 const std::string& attribute_name) const { 213 if (!container()) 214 return std::string(); 215 216 return container()->element().getAttribute( 217 WebKit::WebString::fromUTF8(attribute_name)).utf8(); 218} 219 220bool BrowserPlugin::HasDOMAttribute(const std::string& attribute_name) const { 221 if (!container()) 222 return false; 223 224 return container()->element().hasAttribute( 225 WebKit::WebString::fromUTF8(attribute_name)); 226} 227 228std::string BrowserPlugin::GetNameAttribute() const { 229 return GetDOMAttributeValue(browser_plugin::kAttributeName); 230} 231 232std::string BrowserPlugin::GetSrcAttribute() const { 233 return GetDOMAttributeValue(browser_plugin::kAttributeSrc); 234} 235 236bool BrowserPlugin::GetAutoSizeAttribute() const { 237 return HasDOMAttribute(browser_plugin::kAttributeAutoSize); 238} 239 240int BrowserPlugin::GetMaxHeightAttribute() const { 241 int max_height; 242 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxHeight), 243 &max_height); 244 return max_height; 245} 246 247int BrowserPlugin::GetMaxWidthAttribute() const { 248 int max_width; 249 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxWidth), 250 &max_width); 251 return max_width; 252} 253 254int BrowserPlugin::GetMinHeightAttribute() const { 255 int min_height; 256 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinHeight), 257 &min_height); 258 return min_height; 259} 260 261int BrowserPlugin::GetMinWidthAttribute() const { 262 int min_width; 263 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinWidth), 264 &min_width); 265 return min_width; 266} 267 268int BrowserPlugin::GetAdjustedMaxHeight() const { 269 int max_height = GetMaxHeightAttribute(); 270 return max_height ? max_height : height(); 271} 272 273int BrowserPlugin::GetAdjustedMaxWidth() const { 274 int max_width = GetMaxWidthAttribute(); 275 return max_width ? max_width : width(); 276} 277 278int BrowserPlugin::GetAdjustedMinHeight() const { 279 int min_height = GetMinHeightAttribute(); 280 // FrameView.cpp does not allow this value to be <= 0, so when the value is 281 // unset (or set to 0), we set it to the container size. 282 min_height = min_height ? min_height : height(); 283 // For autosize, minHeight should not be bigger than maxHeight. 284 return std::min(min_height, GetAdjustedMaxHeight()); 285} 286 287int BrowserPlugin::GetAdjustedMinWidth() const { 288 int min_width = GetMinWidthAttribute(); 289 // FrameView.cpp does not allow this value to be <= 0, so when the value is 290 // unset (or set to 0), we set it to the container size. 291 min_width = min_width ? min_width : width(); 292 // For autosize, minWidth should not be bigger than maxWidth. 293 return std::min(min_width, GetAdjustedMaxWidth()); 294} 295 296std::string BrowserPlugin::GetPartitionAttribute() const { 297 return GetDOMAttributeValue(browser_plugin::kAttributePartition); 298} 299 300void BrowserPlugin::ParseNameAttribute() { 301 if (!HasGuest()) 302 return; 303 browser_plugin_manager()->Send( 304 new BrowserPluginHostMsg_SetName(render_view_routing_id_, 305 instance_id_, 306 GetNameAttribute())); 307} 308 309bool BrowserPlugin::ParseSrcAttribute(std::string* error_message) { 310 if (!valid_partition_id_) { 311 *error_message = browser_plugin::kErrorInvalidPartition; 312 return false; 313 } 314 std::string src = GetSrcAttribute(); 315 if (src.empty()) 316 return true; 317 318 // If we haven't created the guest yet, do so now. We will navigate it right 319 // after creation. If |src| is empty, we can delay the creation until we 320 // actually need it. 321 if (!HasGuest()) { 322 // On initial navigation, we request an instance ID from the browser 323 // process. We essentially ignore all subsequent calls to SetSrcAttribute 324 // until we receive an instance ID. |before_first_navigation_| 325 // prevents BrowserPlugin from allocating more than one instance ID. 326 // Upon receiving an instance ID from the browser process, we continue 327 // the process of navigation by populating the 328 // BrowserPluginHostMsg_Attach_Params with the current state of 329 // BrowserPlugin and sending a BrowserPluginHostMsg_CreateGuest to the 330 // browser process in order to create a new guest. 331 if (before_first_navigation_) { 332 browser_plugin_manager()->AllocateInstanceID(this); 333 before_first_navigation_ = false; 334 } 335 return true; 336 } 337 338 browser_plugin_manager()->Send( 339 new BrowserPluginHostMsg_NavigateGuest(render_view_routing_id_, 340 instance_id_, 341 src)); 342 return true; 343} 344 345void BrowserPlugin::ParseAutoSizeAttribute() { 346 auto_size_ack_pending_ = true; 347 last_view_size_ = plugin_rect_.size(); 348 UpdateGuestAutoSizeState(GetAutoSizeAttribute()); 349} 350 351void BrowserPlugin::PopulateAutoSizeParameters( 352 BrowserPluginHostMsg_AutoSize_Params* params, bool current_auto_size) { 353 params->enable = current_auto_size; 354 // No need to populate the params if autosize is off. 355 if (current_auto_size) { 356 params->max_size = gfx::Size(GetAdjustedMaxWidth(), GetAdjustedMaxHeight()); 357 params->min_size = gfx::Size(GetAdjustedMinWidth(), GetAdjustedMinHeight()); 358 } 359} 360 361void BrowserPlugin::UpdateGuestAutoSizeState(bool current_auto_size) { 362 // If we haven't yet heard back from the guest about the last resize request, 363 // then we don't issue another request until we do in 364 // BrowserPlugin::UpdateRect. 365 if (!HasGuest() || !resize_ack_received_) 366 return; 367 BrowserPluginHostMsg_AutoSize_Params auto_size_params; 368 BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params; 369 if (current_auto_size) { 370 GetDamageBufferWithSizeParams(&auto_size_params, &resize_guest_params); 371 } else { 372 GetDamageBufferWithSizeParams(NULL, &resize_guest_params); 373 } 374 resize_ack_received_ = false; 375 browser_plugin_manager()->Send( 376 new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_, 377 instance_id_, 378 auto_size_params, 379 resize_guest_params)); 380} 381 382void BrowserPlugin::SizeChangedDueToAutoSize(const gfx::Size& old_view_size) { 383 size_changed_in_flight_ = false; 384 385 std::map<std::string, base::Value*> props; 386 props[browser_plugin::kOldHeight] = 387 new base::FundamentalValue(old_view_size.height()); 388 props[browser_plugin::kOldWidth] = 389 new base::FundamentalValue(old_view_size.width()); 390 props[browser_plugin::kNewHeight] = 391 new base::FundamentalValue(last_view_size_.height()); 392 props[browser_plugin::kNewWidth] = 393 new base::FundamentalValue(last_view_size_.width()); 394 TriggerEvent(browser_plugin::kEventSizeChanged, &props); 395} 396 397// static 398bool BrowserPlugin::UsesDamageBuffer( 399 const BrowserPluginMsg_UpdateRect_Params& params) { 400 return params.damage_buffer_sequence_id != 0 || params.needs_ack; 401} 402 403bool BrowserPlugin::UsesPendingDamageBuffer( 404 const BrowserPluginMsg_UpdateRect_Params& params) { 405 if (!pending_damage_buffer_) 406 return false; 407 return damage_buffer_sequence_id_ == params.damage_buffer_sequence_id; 408} 409 410void BrowserPlugin::Attach(int instance_id) { 411 CHECK(instance_id != browser_plugin::kInstanceIDNone); 412 before_first_navigation_ = false; 413 instance_id_ = instance_id; 414 browser_plugin_manager()->AddBrowserPlugin(instance_id, this); 415 416 BrowserPluginHostMsg_Attach_Params create_guest_params; 417 create_guest_params.focused = ShouldGuestBeFocused(); 418 create_guest_params.visible = visible_; 419 create_guest_params.name = GetNameAttribute(); 420 create_guest_params.storage_partition_id = storage_partition_id_; 421 create_guest_params.persist_storage = persist_storage_; 422 create_guest_params.src = GetSrcAttribute(); 423 GetDamageBufferWithSizeParams(&create_guest_params.auto_size_params, 424 &create_guest_params.resize_guest_params); 425 426 browser_plugin_manager()->Send( 427 new BrowserPluginHostMsg_Attach(render_view_routing_id_, 428 instance_id_, create_guest_params)); 429} 430 431void BrowserPlugin::DidCommitCompositorFrame() { 432 if (compositing_helper_) 433 compositing_helper_->DidCommitCompositorFrame(); 434} 435 436void BrowserPlugin::OnAddMessageToConsole( 437 int instance_id, const base::DictionaryValue& message_info) { 438 std::map<std::string, base::Value*> props; 439 // Fill in the info provided by the browser. 440 for (DictionaryValue::Iterator iter(message_info); !iter.IsAtEnd(); 441 iter.Advance()) { 442 props[iter.key()] = iter.value().DeepCopy(); 443 } 444 TriggerEvent(browser_plugin::kEventConsoleMessage, &props); 445} 446 447void BrowserPlugin::OnAdvanceFocus(int instance_id, bool reverse) { 448 DCHECK(render_view_); 449 render_view_->GetWebView()->advanceFocus(reverse); 450} 451 452void BrowserPlugin::OnAttachACK( 453 int instance_id, 454 const BrowserPluginMsg_Attach_ACK_Params& params) { 455 // Update BrowserPlugin attributes to match the state of the guest. 456 if (!params.name.empty()) 457 OnUpdatedName(instance_id, params.name); 458 if (!params.storage_partition_id.empty()) { 459 std::string partition_name = 460 (params.persist_storage ? browser_plugin::kPersistPrefix : "") + 461 params.storage_partition_id; 462 UpdateDOMAttribute(browser_plugin::kAttributePartition, partition_name); 463 } 464} 465 466void BrowserPlugin::OnBuffersSwapped(int instance_id, 467 const gfx::Size& size, 468 std::string mailbox_name, 469 int gpu_route_id, 470 int gpu_host_id) { 471 DCHECK(instance_id == instance_id_); 472 EnableCompositing(true); 473 474 compositing_helper_->OnBuffersSwapped(size, 475 mailbox_name, 476 gpu_route_id, 477 gpu_host_id, 478 GetDeviceScaleFactor()); 479} 480 481void BrowserPlugin::OnClose(int instance_id) { 482 TriggerEvent(browser_plugin::kEventClose, NULL); 483} 484 485void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) { 486 BrowserPluginMsg_CompositorFrameSwapped::Param param; 487 if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, ¶m)) 488 return; 489 scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); 490 param.b.AssignTo(frame.get()); 491 492 EnableCompositing(true); 493 compositing_helper_->OnCompositorFrameSwapped(frame.Pass(), 494 param.c /* route_id */, 495 param.d /* host_id */); 496} 497 498void BrowserPlugin::OnGuestContentWindowReady(int instance_id, 499 int content_window_routing_id) { 500 DCHECK(content_window_routing_id != MSG_ROUTING_NONE); 501 content_window_routing_id_ = content_window_routing_id; 502} 503 504void BrowserPlugin::OnGuestGone(int instance_id, int process_id, int status) { 505 // Set the BrowserPlugin in a crashed state before firing event listeners so 506 // that operations on the BrowserPlugin within listeners are aware that 507 // BrowserPlugin is in a crashed state. 508 guest_crashed_ = true; 509 510 // We fire the event listeners before painting the sad graphic to give the 511 // developer an opportunity to display an alternative overlay image on crash. 512 std::string termination_status = TerminationStatusToString( 513 static_cast<base::TerminationStatus>(status)); 514 std::map<std::string, base::Value*> props; 515 props[browser_plugin::kProcessId] = new base::FundamentalValue(process_id); 516 props[browser_plugin::kReason] = new base::StringValue(termination_status); 517 518 // Event listeners may remove the BrowserPlugin from the document. If that 519 // happens, the BrowserPlugin will be scheduled for later deletion (see 520 // BrowserPlugin::destroy()). That will clear the container_ reference, 521 // but leave other member variables valid below. 522 TriggerEvent(browser_plugin::kEventExit, &props); 523 524 // We won't paint the contents of the current backing store again so we might 525 // as well toss it out and save memory. 526 backing_store_.reset(); 527 // If the BrowserPlugin is scheduled to be deleted, then container_ will be 528 // NULL so we shouldn't attempt to access it. 529 if (container_) 530 container_->invalidate(); 531 // Turn off compositing so we can display the sad graphic. 532 EnableCompositing(false); 533} 534 535void BrowserPlugin::OnGuestResponsive(int instance_id, int process_id) { 536 std::map<std::string, base::Value*> props; 537 props[browser_plugin::kProcessId] = new base::FundamentalValue(process_id); 538 TriggerEvent(browser_plugin::kEventResponsive, &props); 539} 540 541void BrowserPlugin::OnGuestUnresponsive(int instance_id, int process_id) { 542 std::map<std::string, base::Value*> props; 543 props[browser_plugin::kProcessId] = new base::FundamentalValue(process_id); 544 TriggerEvent(browser_plugin::kEventUnresponsive, &props); 545} 546 547void BrowserPlugin::OnLoadAbort(int instance_id, 548 const GURL& url, 549 bool is_top_level, 550 const std::string& type) { 551 std::map<std::string, base::Value*> props; 552 props[browser_plugin::kURL] = new base::StringValue(url.spec()); 553 props[browser_plugin::kIsTopLevel] = new base::FundamentalValue(is_top_level); 554 props[browser_plugin::kReason] = new base::StringValue(type); 555 TriggerEvent(browser_plugin::kEventLoadAbort, &props); 556} 557 558void BrowserPlugin::OnLoadCommit( 559 int instance_id, 560 const BrowserPluginMsg_LoadCommit_Params& params) { 561 // If the guest has just committed a new navigation then it is no longer 562 // crashed. 563 guest_crashed_ = false; 564 if (params.is_top_level) 565 UpdateDOMAttribute(browser_plugin::kAttributeSrc, params.url.spec()); 566 567 guest_process_id_ = params.process_id; 568 guest_route_id_ = params.route_id; 569 current_nav_entry_index_ = params.current_entry_index; 570 nav_entry_count_ = params.entry_count; 571 572 std::map<std::string, base::Value*> props; 573 props[browser_plugin::kURL] = new base::StringValue(params.url.spec()); 574 props[browser_plugin::kIsTopLevel] = 575 new base::FundamentalValue(params.is_top_level); 576 TriggerEvent(browser_plugin::kEventLoadCommit, &props); 577} 578 579void BrowserPlugin::OnLoadRedirect(int instance_id, 580 const GURL& old_url, 581 const GURL& new_url, 582 bool is_top_level) { 583 std::map<std::string, base::Value*> props; 584 props[browser_plugin::kOldURL] = new base::StringValue(old_url.spec()); 585 props[browser_plugin::kNewURL] = new base::StringValue(new_url.spec()); 586 props[browser_plugin::kIsTopLevel] = new base::FundamentalValue(is_top_level); 587 TriggerEvent(browser_plugin::kEventLoadRedirect, &props); 588} 589 590void BrowserPlugin::OnLoadStart(int instance_id, 591 const GURL& url, 592 bool is_top_level) { 593 std::map<std::string, base::Value*> props; 594 props[browser_plugin::kURL] = new base::StringValue(url.spec()); 595 props[browser_plugin::kIsTopLevel] = new base::FundamentalValue(is_top_level); 596 597 TriggerEvent(browser_plugin::kEventLoadStart, &props); 598} 599 600void BrowserPlugin::OnLoadStop(int instance_id) { 601 TriggerEvent(browser_plugin::kEventLoadStop, NULL); 602} 603 604void BrowserPlugin::OnRequestPermission( 605 int instance_id, 606 BrowserPluginPermissionType permission_type, 607 int request_id, 608 const base::DictionaryValue& request_info) { 609 // The New Window API is very similiar to the permission API in structure, 610 // but exposes a slightly different interface to the developer and so we put 611 // it in a separate event. 612 const char* event_name = 613 (permission_type == BrowserPluginPermissionTypeNewWindow) ? 614 browser_plugin::kEventNewWindow : 615 browser_plugin::kEventRequestPermission; 616 617 AddPermissionRequestToMap(request_id, permission_type); 618 619 std::map<std::string, base::Value*> props; 620 props[browser_plugin::kPermission] = 621 base::Value::CreateStringValue(PermissionTypeToString(permission_type)); 622 props[browser_plugin::kRequestId] = 623 base::Value::CreateIntegerValue(request_id); 624 625 // Fill in the info provided by the browser. 626 for (DictionaryValue::Iterator iter(request_info); !iter.IsAtEnd(); 627 iter.Advance()) { 628 props[iter.key()] = iter.value().DeepCopy(); 629 } 630 TriggerEvent(event_name, &props); 631} 632 633void BrowserPlugin::OnSetCursor(int instance_id, const WebCursor& cursor) { 634 cursor_ = cursor; 635} 636 637void BrowserPlugin::OnShouldAcceptTouchEvents(int instance_id, bool accept) { 638 if (container()) { 639 container()->requestTouchEventType(accept ? 640 WebKit::WebPluginContainer::TouchEventRequestTypeRaw : 641 WebKit::WebPluginContainer::TouchEventRequestTypeNone); 642 } 643} 644 645void BrowserPlugin::OnUnlockMouse(int instance_id) { 646 render_view_->mouse_lock_dispatcher()->UnlockMouse(this); 647} 648 649void BrowserPlugin::OnUpdatedName(int instance_id, const std::string& name) { 650 UpdateDOMAttribute(browser_plugin::kAttributeName, name); 651} 652 653void BrowserPlugin::AddPermissionRequestToMap(int request_id, 654 BrowserPluginPermissionType type) { 655 DCHECK(!pending_permission_requests_.count(request_id)); 656 pending_permission_requests_.insert(std::make_pair(request_id, type)); 657} 658 659void BrowserPlugin::OnUpdateRect( 660 int instance_id, 661 const BrowserPluginMsg_UpdateRect_Params& params) { 662 bool use_new_damage_buffer = !backing_store_; 663 BrowserPluginHostMsg_AutoSize_Params auto_size_params; 664 BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params; 665 // If we have a pending damage buffer, and the guest has begun to use the 666 // damage buffer then we know the guest will no longer use the current 667 // damage buffer. At this point, we drop the current damage buffer, and 668 // mark the pending damage buffer as the current damage buffer. 669 if (UsesPendingDamageBuffer(params)) { 670 SwapDamageBuffers(); 671 use_new_damage_buffer = true; 672 } 673 674 bool auto_size = GetAutoSizeAttribute(); 675 // We receive a resize ACK in regular mode, but not in autosize. 676 // In SW, |resize_ack_received_| is reset in SwapDamageBuffers(). 677 // In HW mode, we need to do it here so we can continue sending 678 // resize messages when needed. 679 if (params.is_resize_ack || 680 (!params.needs_ack && (auto_size || auto_size_ack_pending_))) 681 resize_ack_received_ = true; 682 683 auto_size_ack_pending_ = false; 684 685 if ((!auto_size && (width() != params.view_size.width() || 686 height() != params.view_size.height())) || 687 (auto_size && (!InAutoSizeBounds(params.view_size))) || 688 GetDeviceScaleFactor() != params.scale_factor) { 689 // We are HW accelerated, render widget does not expect an ack, 690 // but we still need to update the size. 691 if (!params.needs_ack) { 692 UpdateGuestAutoSizeState(auto_size); 693 return; 694 } 695 696 if (!resize_ack_received_) { 697 // The guest has not yet responded to the last resize request, and 698 // so we don't want to do anything at this point other than ACK the guest. 699 if (auto_size) 700 PopulateAutoSizeParameters(&auto_size_params, auto_size); 701 } else { 702 // If we have no pending damage buffer, then the guest has not caught up 703 // with the BrowserPlugin container. We now tell the guest about the new 704 // container size. 705 if (auto_size) { 706 GetDamageBufferWithSizeParams(&auto_size_params, 707 &resize_guest_params); 708 } else { 709 GetDamageBufferWithSizeParams(NULL, &resize_guest_params); 710 } 711 } 712 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK( 713 render_view_routing_id_, 714 instance_id_, 715 auto_size_params, 716 resize_guest_params)); 717 return; 718 } 719 720 if (auto_size && (params.view_size != last_view_size_)) { 721 if (backing_store_) 722 backing_store_->Clear(SK_ColorWHITE); 723 gfx::Size old_view_size = last_view_size_; 724 last_view_size_ = params.view_size; 725 // Schedule a SizeChanged instead of calling it directly to ensure that 726 // the backing store has been updated before the developer attempts to 727 // resize to avoid flicker. |size_changed_in_flight_| acts as a form of 728 // flow control for SizeChanged events. If the guest's view size is changing 729 // rapidly before a SizeChanged event fires, then we avoid scheduling 730 // another SizeChanged event. SizeChanged reads the new size from 731 // |last_view_size_| so we can be sure that it always fires an event 732 // with the last seen view size. 733 if (container_ && !size_changed_in_flight_) { 734 size_changed_in_flight_ = true; 735 base::MessageLoop::current()->PostTask( 736 FROM_HERE, 737 base::Bind(&BrowserPlugin::SizeChangedDueToAutoSize, 738 base::Unretained(this), 739 old_view_size)); 740 } 741 } 742 743 // No more work to do since the guest is no longer using a damage buffer. 744 if (!UsesDamageBuffer(params)) 745 return; 746 747 // If we are seeing damage buffers, HW compositing should be turned off. 748 EnableCompositing(false); 749 750 // If we are now using a new damage buffer, then that means that the guest 751 // has updated its size state in response to a resize request. We change 752 // the backing store's size to accomodate the new damage buffer size. 753 if (use_new_damage_buffer) { 754 int backing_store_width = auto_size ? GetAdjustedMaxWidth() : width(); 755 int backing_store_height = auto_size ? GetAdjustedMaxHeight(): height(); 756 backing_store_.reset( 757 new BrowserPluginBackingStore( 758 gfx::Size(backing_store_width, backing_store_height), 759 params.scale_factor)); 760 } 761 762 // Update the backing store. 763 if (!params.scroll_rect.IsEmpty()) { 764 backing_store_->ScrollBackingStore(params.scroll_delta, 765 params.scroll_rect, 766 params.view_size); 767 } 768 backing_store_->PaintToBackingStore(params.bitmap_rect, 769 params.copy_rects, 770 current_damage_buffer_->memory()); 771 772 // Invalidate the container. 773 // If the BrowserPlugin is scheduled to be deleted, then container_ will be 774 // NULL so we shouldn't attempt to access it. 775 if (container_) 776 container_->invalidate(); 777 if (auto_size) 778 PopulateAutoSizeParameters(&auto_size_params, auto_size); 779 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK( 780 render_view_routing_id_, 781 instance_id_, 782 auto_size_params, 783 resize_guest_params)); 784} 785 786void BrowserPlugin::ParseSizeContraintsChanged() { 787 bool auto_size = GetAutoSizeAttribute(); 788 if (auto_size) 789 UpdateGuestAutoSizeState(true); 790} 791 792bool BrowserPlugin::InAutoSizeBounds(const gfx::Size& size) const { 793 return size.width() <= GetAdjustedMaxWidth() && 794 size.height() <= GetAdjustedMaxHeight(); 795} 796 797NPObject* BrowserPlugin::GetContentWindow() const { 798 if (content_window_routing_id_ == MSG_ROUTING_NONE) 799 return NULL; 800 RenderViewImpl* guest_render_view = RenderViewImpl::FromRoutingID( 801 content_window_routing_id_); 802 if (!guest_render_view) 803 return NULL; 804 WebKit::WebFrame* guest_frame = guest_render_view->GetWebView()->mainFrame(); 805 return guest_frame->windowObject(); 806} 807 808// static 809bool BrowserPlugin::AttachWindowTo(const WebKit::WebNode& node, int window_id) { 810 if (node.isNull()) 811 return false; 812 813 if (!node.isElementNode()) 814 return false; 815 816 WebKit::WebElement shim_element = node.toConst<WebKit::WebElement>(); 817 // The shim containing the BrowserPlugin must be attached to a document. 818 if (shim_element.document().isNull()) 819 return false; 820 821 WebKit::WebNode shadow_root = shim_element.shadowRoot(); 822 if (shadow_root.isNull() || !shadow_root.hasChildNodes()) 823 return false; 824 825 WebKit::WebNode plugin_element = shadow_root.firstChild(); 826 WebKit::WebPluginContainer* plugin_container = 827 plugin_element.pluginContainer(); 828 if (!plugin_container) 829 return false; 830 831 BrowserPlugin* browser_plugin = 832 BrowserPlugin::FromContainer(plugin_container); 833 if (!browser_plugin) 834 return false; 835 836 // If the BrowserPlugin already has a guest attached to it then we probably 837 // shouldn't allow attaching a different guest. 838 // TODO(fsamuel): We may wish to support reattaching guests in the future: 839 // http://crbug.com/156219. 840 if (browser_plugin->HasGuest()) 841 return false; 842 843 browser_plugin->Attach(window_id); 844 return true; 845} 846 847bool BrowserPlugin::HasGuest() const { 848 return instance_id_ != browser_plugin::kInstanceIDNone; 849} 850 851bool BrowserPlugin::CanGoBack() const { 852 return nav_entry_count_ > 1 && current_nav_entry_index_ > 0; 853} 854 855bool BrowserPlugin::CanGoForward() const { 856 return current_nav_entry_index_ >= 0 && 857 current_nav_entry_index_ < (nav_entry_count_ - 1); 858} 859 860bool BrowserPlugin::ParsePartitionAttribute(std::string* error_message) { 861 if (!before_first_navigation_) { 862 *error_message = browser_plugin::kErrorAlreadyNavigated; 863 return false; 864 } 865 866 std::string input = GetPartitionAttribute(); 867 868 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on 869 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely 870 // remove the prefix without splicing in the middle of a multi-byte codepoint. 871 // We can use the rest of the string as UTF-8 encoded one. 872 if (StartsWithASCII(input, browser_plugin::kPersistPrefix, true)) { 873 size_t index = input.find(":"); 874 CHECK(index != std::string::npos); 875 // It is safe to do index + 1, since we tested for the full prefix above. 876 input = input.substr(index + 1); 877 if (input.empty()) { 878 valid_partition_id_ = false; 879 *error_message = browser_plugin::kErrorInvalidPartition; 880 return false; 881 } 882 persist_storage_ = true; 883 } else { 884 persist_storage_ = false; 885 } 886 887 valid_partition_id_ = true; 888 storage_partition_id_ = input; 889 return true; 890} 891 892bool BrowserPlugin::CanRemovePartitionAttribute(std::string* error_message) { 893 if (HasGuest()) 894 *error_message = browser_plugin::kErrorCannotRemovePartition; 895 return !HasGuest(); 896} 897 898void BrowserPlugin::ParseAttributes() { 899 // TODO(mthiesse): Handle errors here? 900 std::string error; 901 ParsePartitionAttribute(&error); 902 903 // Parse the 'src' attribute last, as it will set the has_navigated_ flag to 904 // true, which prevents changing the 'partition' attribute. 905 ParseSrcAttribute(&error); 906} 907 908float BrowserPlugin::GetDeviceScaleFactor() const { 909 if (!render_view_) 910 return 1.0f; 911 return render_view_->GetWebView()->deviceScaleFactor(); 912} 913 914void BrowserPlugin::UpdateDeviceScaleFactor(float device_scale_factor) { 915 if (last_device_scale_factor_ == device_scale_factor || !resize_ack_received_) 916 return; 917 918 BrowserPluginHostMsg_ResizeGuest_Params params; 919 PopulateResizeGuestParameters(¶ms, gfx::Size(width(), height())); 920 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest( 921 render_view_routing_id_, 922 instance_id_, 923 params)); 924} 925 926void BrowserPlugin::TriggerEvent(const std::string& event_name, 927 std::map<std::string, base::Value*>* props) { 928 if (!container() || !container()->element().document().frame()) 929 return; 930 v8::HandleScope handle_scope; 931 std::string json_string; 932 if (props) { 933 base::DictionaryValue dict; 934 for (std::map<std::string, base::Value*>::iterator iter = props->begin(), 935 end = props->end(); iter != end; ++iter) { 936 dict.Set(iter->first, iter->second); 937 } 938 939 JSONStringValueSerializer serializer(&json_string); 940 if (!serializer.Serialize(dict)) 941 return; 942 } 943 944 WebKit::WebFrame* frame = container()->element().document().frame(); 945 WebKit::WebDOMEvent dom_event = frame->document().createEvent("CustomEvent"); 946 WebKit::WebDOMCustomEvent event = dom_event.to<WebKit::WebDOMCustomEvent>(); 947 948 // The events triggered directly from the plugin <object> are internal events 949 // whose implementation details can (and likely will) change over time. The 950 // wrapper/shim (e.g. <webview> tag) should receive these events, and expose a 951 // more appropriate (and stable) event to the consumers as part of the API. 952 event.initCustomEvent( 953 WebKit::WebString::fromUTF8(GetInternalEventName(event_name.c_str())), 954 false, false, 955 WebKit::WebSerializedScriptValue::serialize( 956 v8::String::New(json_string.c_str(), json_string.size()))); 957 container()->element().dispatchEvent(event); 958} 959 960void BrowserPlugin::OnRequestObjectGarbageCollected(int request_id) { 961 // Remove from alive objects. 962 std::map<int, AliveV8PermissionRequestItem*>::iterator iter = 963 alive_v8_permission_request_objects_.find(request_id); 964 if (iter != alive_v8_permission_request_objects_.end()) 965 alive_v8_permission_request_objects_.erase(iter); 966 967 // If a decision has not been made for this request yet, deny it. 968 RespondPermissionIfRequestIsPending(request_id, false /*allow*/); 969} 970 971void BrowserPlugin::PersistRequestObject( 972 const NPVariant* request, const std::string& type, int id) { 973 CHECK(alive_v8_permission_request_objects_.find(id) == 974 alive_v8_permission_request_objects_.end()); 975 if (pending_permission_requests_.find(id) == 976 pending_permission_requests_.end()) { 977 return; 978 } 979 980 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 981 v8::Persistent<v8::Value> weak_request( 982 isolate, WebKit::WebBindings::toV8Value(request)); 983 984 AliveV8PermissionRequestItem* new_item = 985 new std::pair<int, base::WeakPtr<BrowserPlugin> >( 986 id, weak_ptr_factory_.GetWeakPtr()); 987 988 std::pair<std::map<int, AliveV8PermissionRequestItem*>::iterator, bool> 989 result = alive_v8_permission_request_objects_.insert( 990 std::make_pair(id, new_item)); 991 CHECK(result.second); // Inserted in the map. 992 AliveV8PermissionRequestItem* request_item = result.first->second; 993 weak_request.MakeWeak(isolate, request_item, WeakCallbackForPersistObject); 994} 995 996// static 997void BrowserPlugin::WeakCallbackForPersistObject( 998 v8::Isolate* isolate, v8::Persistent<v8::Value> object, void* param) { 999 v8::Persistent<v8::Object> persistent_object = 1000 v8::Persistent<v8::Object>::Cast(object); 1001 1002 AliveV8PermissionRequestItem* item_ptr = 1003 static_cast<AliveV8PermissionRequestItem*>(param); 1004 int request_id = item_ptr->first; 1005 base::WeakPtr<BrowserPlugin> plugin = item_ptr->second; 1006 delete item_ptr; 1007 1008 persistent_object.Dispose(isolate); 1009 persistent_object.Clear(); 1010 1011 if (plugin) { 1012 // Asynchronously remove item from |alive_v8_permission_request_objects_|. 1013 // Note that we are using weak pointer for the following PostTask, so we 1014 // don't need to worry about BrowserPlugin going away. 1015 base::MessageLoop::current()->PostTask( 1016 FROM_HERE, 1017 base::Bind(&BrowserPlugin::OnRequestObjectGarbageCollected, 1018 plugin, 1019 request_id)); 1020 } 1021} 1022 1023void BrowserPlugin::Back() { 1024 if (!HasGuest()) 1025 return; 1026 browser_plugin_manager()->Send( 1027 new BrowserPluginHostMsg_Go(render_view_routing_id_, 1028 instance_id_, -1)); 1029} 1030 1031void BrowserPlugin::Forward() { 1032 if (!HasGuest()) 1033 return; 1034 browser_plugin_manager()->Send( 1035 new BrowserPluginHostMsg_Go(render_view_routing_id_, 1036 instance_id_, 1)); 1037} 1038 1039void BrowserPlugin::Go(int relative_index) { 1040 if (!HasGuest()) 1041 return; 1042 browser_plugin_manager()->Send( 1043 new BrowserPluginHostMsg_Go(render_view_routing_id_, 1044 instance_id_, 1045 relative_index)); 1046} 1047 1048void BrowserPlugin::TerminateGuest() { 1049 if (!HasGuest() || guest_crashed_) 1050 return; 1051 browser_plugin_manager()->Send( 1052 new BrowserPluginHostMsg_TerminateGuest(render_view_routing_id_, 1053 instance_id_)); 1054} 1055 1056void BrowserPlugin::Stop() { 1057 if (!HasGuest()) 1058 return; 1059 browser_plugin_manager()->Send( 1060 new BrowserPluginHostMsg_Stop(render_view_routing_id_, 1061 instance_id_)); 1062} 1063 1064void BrowserPlugin::Reload() { 1065 if (!HasGuest()) 1066 return; 1067 browser_plugin_manager()->Send( 1068 new BrowserPluginHostMsg_Reload(render_view_routing_id_, 1069 instance_id_)); 1070} 1071 1072void BrowserPlugin::UpdateGuestFocusState() { 1073 if (!HasGuest()) 1074 return; 1075 bool should_be_focused = ShouldGuestBeFocused(); 1076 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus( 1077 render_view_routing_id_, 1078 instance_id_, 1079 should_be_focused)); 1080} 1081 1082bool BrowserPlugin::ShouldGuestBeFocused() const { 1083 bool embedder_focused = false; 1084 if (render_view_) 1085 embedder_focused = render_view_->has_focus(); 1086 return plugin_focused_ && embedder_focused; 1087} 1088 1089WebKit::WebPluginContainer* BrowserPlugin::container() const { 1090 return container_; 1091} 1092 1093void BrowserPlugin::RespondPermission( 1094 BrowserPluginPermissionType permission_type, int request_id, bool allow) { 1095 if (permission_type == BrowserPluginPermissionTypePointerLock) 1096 RespondPermissionPointerLock(allow); 1097 else 1098 browser_plugin_manager()->Send( 1099 new BrowserPluginHostMsg_RespondPermission( 1100 render_view_->GetRoutingID(), instance_id_, permission_type, 1101 request_id, allow)); 1102} 1103 1104void BrowserPlugin::RespondPermissionPointerLock(bool allow) { 1105 if (allow) 1106 render_view_->mouse_lock_dispatcher()->LockMouse(this); 1107 else 1108 OnLockMouseACK(false); 1109} 1110 1111void BrowserPlugin::RespondPermissionIfRequestIsPending( 1112 int request_id, bool allow) { 1113 PendingPermissionRequests::iterator iter = 1114 pending_permission_requests_.find(request_id); 1115 if (iter == pending_permission_requests_.end()) 1116 return; 1117 1118 BrowserPluginPermissionType permission_type = iter->second; 1119 pending_permission_requests_.erase(iter); 1120 RespondPermission(permission_type, request_id, allow); 1121} 1122 1123void BrowserPlugin::OnEmbedderDecidedPermission(int request_id, bool allow) { 1124 RespondPermissionIfRequestIsPending(request_id, allow); 1125} 1126 1127bool BrowserPlugin::initialize(WebPluginContainer* container) { 1128 if (!container) 1129 return false; 1130 1131 if (!GetContentClient()->renderer()->AllowBrowserPlugin(container)) 1132 return false; 1133 1134 bindings_.reset(new BrowserPluginBindings(this)); 1135 container_ = container; 1136 container_->setWantsWheelEvents(true); 1137 ParseAttributes(); 1138 g_plugin_container_map.Get().insert(std::make_pair(container_, this)); 1139 return true; 1140} 1141 1142void BrowserPlugin::EnableCompositing(bool enable) { 1143 if (compositing_enabled_ == enable) 1144 return; 1145 1146 compositing_enabled_ = enable; 1147 if (enable) { 1148 // No need to keep the backing store and damage buffer around if we're now 1149 // compositing. 1150 backing_store_.reset(); 1151 current_damage_buffer_.reset(); 1152 if (!compositing_helper_) { 1153 compositing_helper_ = new BrowserPluginCompositingHelper( 1154 container_, 1155 browser_plugin_manager(), 1156 instance_id_, 1157 render_view_routing_id_); 1158 } 1159 } else { 1160 // We're switching back to the software path. We create a new damage 1161 // buffer that can accommodate the current size of the container. 1162 BrowserPluginHostMsg_ResizeGuest_Params params; 1163 PopulateResizeGuestParameters(¶ms, gfx::Size(width(), height())); 1164 // Request a full repaint from the guest even if its size is not actually 1165 // changing. 1166 params.repaint = true; 1167 resize_ack_received_ = false; 1168 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest( 1169 render_view_routing_id_, 1170 instance_id_, 1171 params)); 1172 } 1173 compositing_helper_->EnableCompositing(enable); 1174} 1175 1176void BrowserPlugin::destroy() { 1177 // The BrowserPlugin's WebPluginContainer is deleted immediately after this 1178 // call returns, so let's not keep a reference to it around. 1179 g_plugin_container_map.Get().erase(container_); 1180 container_ = NULL; 1181 if (compositing_helper_) 1182 compositing_helper_->OnContainerDestroy(); 1183 // Will be a no-op if the mouse is not currently locked. 1184 if (render_view_) 1185 render_view_->mouse_lock_dispatcher()->OnLockTargetDestroyed(this); 1186 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 1187} 1188 1189NPObject* BrowserPlugin::scriptableObject() { 1190 if (!bindings_) 1191 return NULL; 1192 1193 NPObject* browser_plugin_np_object(bindings_->np_object()); 1194 // The object is expected to be retained before it is returned. 1195 WebKit::WebBindings::retainObject(browser_plugin_np_object); 1196 return browser_plugin_np_object; 1197} 1198 1199bool BrowserPlugin::supportsKeyboardFocus() const { 1200 return true; 1201} 1202 1203bool BrowserPlugin::canProcessDrag() const { 1204 return true; 1205} 1206 1207void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) { 1208 if (guest_crashed_) { 1209 if (!sad_guest_) // Lazily initialize bitmap. 1210 sad_guest_ = content::GetContentClient()->renderer()-> 1211 GetSadWebViewBitmap(); 1212 // content_shell does not have the sad plugin bitmap, so we'll paint black 1213 // instead to make it clear that something went wrong. 1214 if (sad_guest_) { 1215 webkit::PaintSadPlugin(canvas, plugin_rect_, *sad_guest_); 1216 return; 1217 } 1218 } 1219 SkAutoCanvasRestore auto_restore(canvas, true); 1220 canvas->translate(plugin_rect_.x(), plugin_rect_.y()); 1221 SkRect image_data_rect = SkRect::MakeXYWH( 1222 SkIntToScalar(0), 1223 SkIntToScalar(0), 1224 SkIntToScalar(plugin_rect_.width()), 1225 SkIntToScalar(plugin_rect_.height())); 1226 canvas->clipRect(image_data_rect); 1227 // Paint black or white in case we have nothing in our backing store or we 1228 // need to show a gutter. 1229 SkPaint paint; 1230 paint.setStyle(SkPaint::kFill_Style); 1231 paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE); 1232 canvas->drawRect(image_data_rect, paint); 1233 // Stay a solid color if we have never set a non-empty src, or we don't have a 1234 // backing store. 1235 if (!backing_store_.get() || !HasGuest()) 1236 return; 1237 float inverse_scale_factor = 1.0f / backing_store_->GetScaleFactor(); 1238 canvas->scale(inverse_scale_factor, inverse_scale_factor); 1239 canvas->drawBitmap(backing_store_->GetBitmap(), 0, 0); 1240} 1241 1242bool BrowserPlugin::InBounds(const gfx::Point& position) const { 1243 // Note that even for plugins that are rotated using rotate transformations, 1244 // we use the the |plugin_rect_| provided by updateGeometry, which means we 1245 // will be off if |position| is within the plugin rect but does not fall 1246 // within the actual plugin boundary. Not supporting such edge case is OK 1247 // since this function should not be used for making security-sensitive 1248 // decisions. 1249 // This also does not take overlapping plugins into account. 1250 bool result = position.x() >= plugin_rect_.x() && 1251 position.x() < plugin_rect_.x() + plugin_rect_.width() && 1252 position.y() >= plugin_rect_.y() && 1253 position.y() < plugin_rect_.y() + plugin_rect_.height(); 1254 return result; 1255} 1256 1257gfx::Point BrowserPlugin::ToLocalCoordinates(const gfx::Point& point) const { 1258 if (container_) 1259 return container_->windowToLocalPoint(WebKit::WebPoint(point)); 1260 return gfx::Point(point.x() - plugin_rect_.x(), point.y() - plugin_rect_.y()); 1261} 1262 1263// static 1264bool BrowserPlugin::ShouldForwardToBrowserPlugin( 1265 const IPC::Message& message) { 1266 switch (message.type()) { 1267 case BrowserPluginMsg_AddMessageToConsole::ID: 1268 case BrowserPluginMsg_AdvanceFocus::ID: 1269 case BrowserPluginMsg_Attach_ACK::ID: 1270 case BrowserPluginMsg_BuffersSwapped::ID: 1271 case BrowserPluginMsg_Close::ID: 1272 case BrowserPluginMsg_CompositorFrameSwapped::ID: 1273 case BrowserPluginMsg_GuestContentWindowReady::ID: 1274 case BrowserPluginMsg_GuestGone::ID: 1275 case BrowserPluginMsg_GuestResponsive::ID: 1276 case BrowserPluginMsg_GuestUnresponsive::ID: 1277 case BrowserPluginMsg_LoadAbort::ID: 1278 case BrowserPluginMsg_LoadCommit::ID: 1279 case BrowserPluginMsg_LoadRedirect::ID: 1280 case BrowserPluginMsg_LoadStart::ID: 1281 case BrowserPluginMsg_LoadStop::ID: 1282 case BrowserPluginMsg_RequestPermission::ID: 1283 case BrowserPluginMsg_SetCursor::ID: 1284 case BrowserPluginMsg_ShouldAcceptTouchEvents::ID: 1285 case BrowserPluginMsg_UnlockMouse::ID: 1286 case BrowserPluginMsg_UpdatedName::ID: 1287 case BrowserPluginMsg_UpdateRect::ID: 1288 return true; 1289 default: 1290 break; 1291 } 1292 return false; 1293} 1294 1295void BrowserPlugin::updateGeometry( 1296 const WebRect& window_rect, 1297 const WebRect& clip_rect, 1298 const WebVector<WebRect>& cut_outs_rects, 1299 bool is_visible) { 1300 int old_width = width(); 1301 int old_height = height(); 1302 plugin_rect_ = window_rect; 1303 // In AutoSize mode, guests don't care when the BrowserPlugin container is 1304 // resized. If |!resize_ack_received_|, then we are still waiting on a 1305 // previous resize to be ACK'ed and so we don't issue additional resizes 1306 // until the previous one is ACK'ed. 1307 // TODO(mthiesse): Assess the performance of calling GetAutoSizeAttribute() on 1308 // resize. 1309 if (!HasGuest() || 1310 !resize_ack_received_ || 1311 (old_width == window_rect.width && old_height == window_rect.height) || 1312 GetAutoSizeAttribute()) { 1313 return; 1314 } 1315 1316 BrowserPluginHostMsg_ResizeGuest_Params params; 1317 PopulateResizeGuestParameters(¶ms, gfx::Size(width(), height())); 1318 resize_ack_received_ = false; 1319 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest( 1320 render_view_routing_id_, 1321 instance_id_, 1322 params)); 1323} 1324 1325void BrowserPlugin::SwapDamageBuffers() { 1326 current_damage_buffer_.reset(pending_damage_buffer_.release()); 1327 resize_ack_received_ = true; 1328} 1329 1330void BrowserPlugin::PopulateResizeGuestParameters( 1331 BrowserPluginHostMsg_ResizeGuest_Params* params, 1332 const gfx::Size& view_size) { 1333 params->view_size = view_size; 1334 params->scale_factor = GetDeviceScaleFactor(); 1335 if (last_device_scale_factor_ != params->scale_factor){ 1336 params->repaint = true; 1337 last_device_scale_factor_ = params->scale_factor; 1338 } 1339 1340 // In HW compositing mode, we do not need a damage buffer. 1341 if (compositing_enabled_) 1342 return; 1343 1344 const size_t stride = skia::PlatformCanvasStrideForWidth(view_size.width()); 1345 // Make sure the size of the damage buffer is at least four bytes so that we 1346 // can fit in a magic word to verify that the memory is shared correctly. 1347 size_t size = 1348 std::max(sizeof(unsigned int), 1349 static_cast<size_t>(view_size.height() * 1350 stride * 1351 GetDeviceScaleFactor() * 1352 GetDeviceScaleFactor())); 1353 1354 params->damage_buffer_size = size; 1355 pending_damage_buffer_.reset( 1356 CreateDamageBuffer(size, ¶ms->damage_buffer_handle)); 1357 if (!pending_damage_buffer_) 1358 NOTREACHED(); 1359 params->damage_buffer_sequence_id = ++damage_buffer_sequence_id_; 1360} 1361 1362void BrowserPlugin::GetDamageBufferWithSizeParams( 1363 BrowserPluginHostMsg_AutoSize_Params* auto_size_params, 1364 BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params) { 1365 if (auto_size_params) 1366 PopulateAutoSizeParameters(auto_size_params, GetAutoSizeAttribute()); 1367 gfx::Size view_size = (auto_size_params && auto_size_params->enable) ? 1368 auto_size_params->max_size : gfx::Size(width(), height()); 1369 if (view_size.IsEmpty()) 1370 return; 1371 resize_ack_received_ = false; 1372 PopulateResizeGuestParameters(resize_guest_params, view_size); 1373} 1374 1375#if defined(OS_POSIX) 1376base::SharedMemory* BrowserPlugin::CreateDamageBuffer( 1377 const size_t size, 1378 base::SharedMemoryHandle* damage_buffer_handle) { 1379 scoped_ptr<base::SharedMemory> shared_buf( 1380 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer( 1381 size).release()); 1382 1383 if (shared_buf) { 1384 if (shared_buf->Map(size)) { 1385 // Insert the magic word. 1386 *static_cast<unsigned int*>(shared_buf->memory()) = 0xdeadbeef; 1387 shared_buf->ShareToProcess(base::GetCurrentProcessHandle(), 1388 damage_buffer_handle); 1389 return shared_buf.release(); 1390 } 1391 } 1392 NOTREACHED(); 1393 return NULL; 1394} 1395#elif defined(OS_WIN) 1396base::SharedMemory* BrowserPlugin::CreateDamageBuffer( 1397 const size_t size, 1398 base::SharedMemoryHandle* damage_buffer_handle) { 1399 scoped_ptr<base::SharedMemory> shared_buf(new base::SharedMemory()); 1400 1401 if (!shared_buf->CreateAndMapAnonymous(size)) { 1402 NOTREACHED() << "Buffer allocation failed"; 1403 return NULL; 1404 } 1405 1406 // Insert the magic word. 1407 *static_cast<unsigned int*>(shared_buf->memory()) = 0xdeadbeef; 1408 if (shared_buf->ShareToProcess(base::GetCurrentProcessHandle(), 1409 damage_buffer_handle)) 1410 return shared_buf.release(); 1411 NOTREACHED(); 1412 return NULL; 1413} 1414#endif 1415 1416void BrowserPlugin::updateFocus(bool focused) { 1417 if (plugin_focused_ == focused) 1418 return; 1419 1420 bool old_guest_focus_state = ShouldGuestBeFocused(); 1421 plugin_focused_ = focused; 1422 1423 if (ShouldGuestBeFocused() != old_guest_focus_state) 1424 UpdateGuestFocusState(); 1425} 1426 1427void BrowserPlugin::updateVisibility(bool visible) { 1428 if (visible_ == visible) 1429 return; 1430 1431 visible_ = visible; 1432 if (!HasGuest()) 1433 return; 1434 1435 if (compositing_helper_) 1436 compositing_helper_->UpdateVisibility(visible); 1437 1438 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility( 1439 render_view_routing_id_, 1440 instance_id_, 1441 visible)); 1442} 1443 1444bool BrowserPlugin::acceptsInputEvents() { 1445 return true; 1446} 1447 1448bool BrowserPlugin::handleInputEvent(const WebKit::WebInputEvent& event, 1449 WebKit::WebCursorInfo& cursor_info) { 1450 if (guest_crashed_ || !HasGuest() || 1451 event.type == WebKit::WebInputEvent::ContextMenu) 1452 return false; 1453 1454 const WebKit::WebInputEvent* modified_event = &event; 1455 scoped_ptr<WebKit::WebTouchEvent> touch_event; 1456 // WebKit gives BrowserPlugin a list of touches that are down, but the browser 1457 // process expects a list of all touches. We modify the TouchEnd event here to 1458 // match these expectations. 1459 if (event.type == WebKit::WebInputEvent::TouchEnd) { 1460 const WebKit::WebTouchEvent* orig_touch_event = 1461 static_cast<const WebKit::WebTouchEvent*>(&event); 1462 touch_event.reset(new WebKit::WebTouchEvent()); 1463 memcpy(touch_event.get(), orig_touch_event, sizeof(WebKit::WebTouchEvent)); 1464 if (touch_event->changedTouchesLength > 0) { 1465 memcpy(&touch_event->touches[touch_event->touchesLength], 1466 &touch_event->changedTouches, 1467 touch_event->changedTouchesLength * sizeof(WebKit::WebTouchPoint)); 1468 } 1469 touch_event->touchesLength += touch_event->changedTouchesLength; 1470 modified_event = touch_event.get(); 1471 } 1472 browser_plugin_manager()->Send( 1473 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_, 1474 instance_id_, 1475 plugin_rect_, 1476 modified_event)); 1477 cursor_.GetCursorInfo(&cursor_info); 1478 return true; 1479} 1480 1481bool BrowserPlugin::handleDragStatusUpdate(WebKit::WebDragStatus drag_status, 1482 const WebKit::WebDragData& drag_data, 1483 WebKit::WebDragOperationsMask mask, 1484 const WebKit::WebPoint& position, 1485 const WebKit::WebPoint& screen) { 1486 if (guest_crashed_ || !HasGuest()) 1487 return false; 1488 browser_plugin_manager()->Send( 1489 new BrowserPluginHostMsg_DragStatusUpdate( 1490 render_view_routing_id_, 1491 instance_id_, 1492 drag_status, 1493 WebDropData(drag_data), 1494 mask, 1495 position)); 1496 return true; 1497} 1498 1499void BrowserPlugin::didReceiveResponse( 1500 const WebKit::WebURLResponse& response) { 1501} 1502 1503void BrowserPlugin::didReceiveData(const char* data, int data_length) { 1504} 1505 1506void BrowserPlugin::didFinishLoading() { 1507} 1508 1509void BrowserPlugin::didFailLoading(const WebKit::WebURLError& error) { 1510} 1511 1512void BrowserPlugin::didFinishLoadingFrameRequest(const WebKit::WebURL& url, 1513 void* notify_data) { 1514} 1515 1516void BrowserPlugin::didFailLoadingFrameRequest( 1517 const WebKit::WebURL& url, 1518 void* notify_data, 1519 const WebKit::WebURLError& error) { 1520} 1521 1522bool BrowserPlugin::executeEditCommand(const WebKit::WebString& name) { 1523 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand( 1524 render_view_routing_id_, 1525 instance_id_, 1526 name.utf8())); 1527 1528 // BrowserPlugin swallows edit commands. 1529 return true; 1530} 1531 1532void BrowserPlugin::OnLockMouseACK(bool succeeded) { 1533 browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK( 1534 render_view_routing_id_, 1535 instance_id_, 1536 succeeded)); 1537} 1538 1539void BrowserPlugin::OnMouseLockLost() { 1540 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK( 1541 render_view_routing_id_, 1542 instance_id_)); 1543} 1544 1545bool BrowserPlugin::HandleMouseLockedInputEvent( 1546 const WebKit::WebMouseEvent& event) { 1547 browser_plugin_manager()->Send( 1548 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_, 1549 instance_id_, 1550 plugin_rect_, 1551 &event)); 1552 return true; 1553} 1554 1555} // namespace content 1556