1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "extensions/browser/guest_view/guest_view_base.h" 6 7#include "base/lazy_instance.h" 8#include "base/strings/utf_string_conversions.h" 9#include "content/public/browser/render_frame_host.h" 10#include "content/public/browser/render_process_host.h" 11#include "content/public/browser/render_view_host.h" 12#include "content/public/browser/web_contents.h" 13#include "content/public/common/url_constants.h" 14#include "extensions/browser/api/extensions_api_client.h" 15#include "extensions/browser/event_router.h" 16#include "extensions/browser/extension_registry.h" 17#include "extensions/browser/guest_view/app_view/app_view_guest.h" 18#include "extensions/browser/guest_view/extension_options/extension_options_guest.h" 19#include "extensions/browser/guest_view/guest_view_manager.h" 20#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" 21#include "extensions/browser/guest_view/web_view/web_view_guest.h" 22#include "extensions/browser/process_map.h" 23#include "extensions/common/extension_messages.h" 24#include "extensions/common/features/feature.h" 25#include "extensions/common/features/feature_provider.h" 26#include "extensions/common/guest_view/guest_view_constants.h" 27#include "third_party/WebKit/public/web/WebInputEvent.h" 28 29using content::WebContents; 30 31namespace extensions { 32 33namespace { 34 35typedef std::map<std::string, GuestViewBase::GuestCreationCallback> 36 GuestViewCreationMap; 37static base::LazyInstance<GuestViewCreationMap> guest_view_registry = 38 LAZY_INSTANCE_INITIALIZER; 39 40typedef std::map<WebContents*, GuestViewBase*> WebContentsGuestViewMap; 41static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map = 42 LAZY_INSTANCE_INITIALIZER; 43 44} // namespace 45 46GuestViewBase::Event::Event(const std::string& name, 47 scoped_ptr<base::DictionaryValue> args) 48 : name_(name), args_(args.Pass()) { 49} 50 51GuestViewBase::Event::~Event() { 52} 53 54scoped_ptr<base::DictionaryValue> GuestViewBase::Event::GetArguments() { 55 return args_.Pass(); 56} 57 58// This observer ensures that the GuestViewBase destroys itself when its 59// embedder goes away. 60class GuestViewBase::EmbedderWebContentsObserver : public WebContentsObserver { 61 public: 62 explicit EmbedderWebContentsObserver(GuestViewBase* guest) 63 : WebContentsObserver(guest->embedder_web_contents()), 64 destroyed_(false), 65 guest_(guest) { 66 } 67 68 virtual ~EmbedderWebContentsObserver() { 69 } 70 71 // WebContentsObserver implementation. 72 virtual void WebContentsDestroyed() OVERRIDE { 73 Destroy(); 74 } 75 76 virtual void RenderViewHostChanged( 77 content::RenderViewHost* old_host, 78 content::RenderViewHost* new_host) OVERRIDE { 79 Destroy(); 80 } 81 82 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE { 83 Destroy(); 84 } 85 86 private: 87 bool destroyed_; 88 GuestViewBase* guest_; 89 90 void Destroy() { 91 if (destroyed_) 92 return; 93 destroyed_ = true; 94 guest_->embedder_web_contents_ = NULL; 95 guest_->EmbedderDestroyed(); 96 guest_->Destroy(); 97 } 98 99 DISALLOW_COPY_AND_ASSIGN(EmbedderWebContentsObserver); 100}; 101 102GuestViewBase::GuestViewBase(content::BrowserContext* browser_context, 103 int guest_instance_id) 104 : embedder_web_contents_(NULL), 105 embedder_render_process_id_(0), 106 browser_context_(browser_context), 107 guest_instance_id_(guest_instance_id), 108 view_instance_id_(guestview::kInstanceIDNone), 109 element_instance_id_(guestview::kInstanceIDNone), 110 initialized_(false), 111 auto_size_enabled_(false), 112 weak_ptr_factory_(this) { 113} 114 115void GuestViewBase::Init(const std::string& embedder_extension_id, 116 content::WebContents* embedder_web_contents, 117 const base::DictionaryValue& create_params, 118 const WebContentsCreatedCallback& callback) { 119 if (initialized_) 120 return; 121 initialized_ = true; 122 123 Feature* feature = FeatureProvider::GetAPIFeatures()->GetFeature( 124 GetAPINamespace()); 125 CHECK(feature); 126 127 ProcessMap* process_map = ProcessMap::Get(browser_context()); 128 CHECK(process_map); 129 130 const Extension* embedder_extension = ExtensionRegistry::Get(browser_context_) 131 ->enabled_extensions() 132 .GetByID(embedder_extension_id); 133 // Ok for |embedder_extension| to be NULL, the embedder might be WebUI. 134 135 CHECK(embedder_web_contents); 136 int embedder_process_id = 137 embedder_web_contents->GetRenderProcessHost()->GetID(); 138 139 const GURL& embedder_site_url = embedder_web_contents->GetLastCommittedURL(); 140 Feature::Availability availability = feature->IsAvailableToContext( 141 embedder_extension, 142 process_map->GetMostLikelyContextType(embedder_extension, 143 embedder_process_id), 144 embedder_site_url); 145 if (!availability.is_available()) { 146 // The derived class did not create a WebContents so this class serves no 147 // purpose. Let's self-destruct. 148 delete this; 149 callback.Run(NULL); 150 return; 151 } 152 153 CreateWebContents(embedder_extension_id, 154 embedder_process_id, 155 embedder_site_url, 156 create_params, 157 base::Bind(&GuestViewBase::CompleteInit, 158 AsWeakPtr(), 159 embedder_extension_id, 160 embedder_process_id, 161 callback)); 162} 163 164void GuestViewBase::InitWithWebContents( 165 const std::string& embedder_extension_id, 166 int embedder_render_process_id, 167 content::WebContents* guest_web_contents) { 168 DCHECK(guest_web_contents); 169 content::RenderProcessHost* embedder_render_process_host = 170 content::RenderProcessHost::FromID(embedder_render_process_id); 171 172 embedder_extension_id_ = embedder_extension_id; 173 embedder_render_process_id_ = embedder_render_process_host->GetID(); 174 embedder_render_process_host->AddObserver(this); 175 176 WebContentsObserver::Observe(guest_web_contents); 177 guest_web_contents->SetDelegate(this); 178 webcontents_guestview_map.Get().insert( 179 std::make_pair(guest_web_contents, this)); 180 GuestViewManager::FromBrowserContext(browser_context_)-> 181 AddGuest(guest_instance_id_, guest_web_contents); 182 183 // Give the derived class an opportunity to perform additional initialization. 184 DidInitialize(); 185} 186 187void GuestViewBase::SetAutoSize(bool enabled, 188 const gfx::Size& min_size, 189 const gfx::Size& max_size) { 190 min_auto_size_ = min_size; 191 min_auto_size_.SetToMin(max_size); 192 max_auto_size_ = max_size; 193 max_auto_size_.SetToMax(min_size); 194 195 enabled &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty() && 196 IsAutoSizeSupported(); 197 if (!enabled && !auto_size_enabled_) 198 return; 199 200 auto_size_enabled_ = enabled; 201 202 if (!attached()) 203 return; 204 205 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); 206 if (auto_size_enabled_) { 207 rvh->EnableAutoResize(min_auto_size_, max_auto_size_); 208 } else { 209 rvh->DisableAutoResize(element_size_); 210 guest_size_ = element_size_; 211 GuestSizeChangedDueToAutoSize(guest_size_, element_size_); 212 } 213} 214 215// static 216void GuestViewBase::RegisterGuestViewType( 217 const std::string& view_type, 218 const GuestCreationCallback& callback) { 219 GuestViewCreationMap::iterator it = 220 guest_view_registry.Get().find(view_type); 221 DCHECK(it == guest_view_registry.Get().end()); 222 guest_view_registry.Get()[view_type] = callback; 223} 224 225// static 226GuestViewBase* GuestViewBase::Create( 227 content::BrowserContext* browser_context, 228 int guest_instance_id, 229 const std::string& view_type) { 230 if (guest_view_registry.Get().empty()) 231 RegisterGuestViewTypes(); 232 233 GuestViewCreationMap::iterator it = 234 guest_view_registry.Get().find(view_type); 235 if (it == guest_view_registry.Get().end()) { 236 NOTREACHED(); 237 return NULL; 238 } 239 return it->second.Run(browser_context, guest_instance_id); 240} 241 242// static 243GuestViewBase* GuestViewBase::FromWebContents(WebContents* web_contents) { 244 WebContentsGuestViewMap* guest_map = webcontents_guestview_map.Pointer(); 245 WebContentsGuestViewMap::iterator it = guest_map->find(web_contents); 246 return it == guest_map->end() ? NULL : it->second; 247} 248 249// static 250GuestViewBase* GuestViewBase::From(int embedder_process_id, 251 int guest_instance_id) { 252 content::RenderProcessHost* host = 253 content::RenderProcessHost::FromID(embedder_process_id); 254 if (!host) 255 return NULL; 256 257 content::WebContents* guest_web_contents = 258 GuestViewManager::FromBrowserContext(host->GetBrowserContext())-> 259 GetGuestByInstanceIDSafely(guest_instance_id, embedder_process_id); 260 if (!guest_web_contents) 261 return NULL; 262 263 return GuestViewBase::FromWebContents(guest_web_contents); 264} 265 266// static 267bool GuestViewBase::IsGuest(WebContents* web_contents) { 268 return !!GuestViewBase::FromWebContents(web_contents); 269} 270 271base::WeakPtr<GuestViewBase> GuestViewBase::AsWeakPtr() { 272 return weak_ptr_factory_.GetWeakPtr(); 273} 274 275bool GuestViewBase::IsAutoSizeSupported() const { 276 return false; 277} 278 279bool GuestViewBase::IsDragAndDropEnabled() const { 280 return false; 281} 282 283void GuestViewBase::RenderProcessExited(content::RenderProcessHost* host, 284 base::ProcessHandle handle, 285 base::TerminationStatus status, 286 int exit_code) { 287 // GuestViewBase tracks the lifetime of its embedder render process until it 288 // is attached to a particular embedder WebContents. At that point, its 289 // lifetime is restricted in scope to the lifetime of its embedder 290 // WebContents. 291 CHECK(!attached()); 292 CHECK_EQ(host->GetID(), embedder_render_process_id()); 293 294 // This code path may be reached if the embedder WebContents is killed for 295 // whatever reason immediately after a called to GuestViewInternal.createGuest 296 // and before attaching the new guest to a frame. 297 Destroy(); 298} 299 300void GuestViewBase::Destroy() { 301 DCHECK(web_contents()); 302 content::RenderProcessHost* host = 303 content::RenderProcessHost::FromID(embedder_render_process_id()); 304 if (host) 305 host->RemoveObserver(this); 306 WillDestroy(); 307 if (!destruction_callback_.is_null()) 308 destruction_callback_.Run(); 309 310 webcontents_guestview_map.Get().erase(web_contents()); 311 GuestViewManager::FromBrowserContext(browser_context_)-> 312 RemoveGuest(guest_instance_id_); 313 pending_events_.clear(); 314 315 delete web_contents(); 316} 317 318void GuestViewBase::DidAttach(int guest_proxy_routing_id) { 319 // Give the derived class an opportunity to perform some actions. 320 DidAttachToEmbedder(); 321 322 // Inform the associated GuestViewContainer that the contentWindow is ready. 323 embedder_web_contents()->Send(new ExtensionMsg_GuestAttached( 324 embedder_web_contents()->GetMainFrame()->GetRoutingID(), 325 element_instance_id_, 326 guest_proxy_routing_id)); 327 328 SendQueuedEvents(); 329} 330 331void GuestViewBase::ElementSizeChanged(const gfx::Size& old_size, 332 const gfx::Size& new_size) { 333 element_size_ = new_size; 334} 335 336void GuestViewBase::GuestSizeChanged(const gfx::Size& old_size, 337 const gfx::Size& new_size) { 338 if (!auto_size_enabled_) 339 return; 340 guest_size_ = new_size; 341 GuestSizeChangedDueToAutoSize(old_size, new_size); 342} 343 344void GuestViewBase::SetAttachParams(const base::DictionaryValue& params) { 345 attach_params_.reset(params.DeepCopy()); 346 attach_params_->GetInteger(guestview::kParameterInstanceId, 347 &view_instance_id_); 348} 349 350void GuestViewBase::SetOpener(GuestViewBase* guest) { 351 if (guest && guest->IsViewType(GetViewType())) { 352 opener_ = guest->AsWeakPtr(); 353 return; 354 } 355 opener_ = base::WeakPtr<GuestViewBase>(); 356} 357 358void GuestViewBase::RegisterDestructionCallback( 359 const DestructionCallback& callback) { 360 destruction_callback_ = callback; 361} 362 363void GuestViewBase::WillAttach(content::WebContents* embedder_web_contents, 364 int element_instance_id) { 365 // After attachment, this GuestViewBase's lifetime is restricted to the 366 // lifetime of its embedder WebContents. Observing the RenderProcessHost 367 // of the embedder is no longer necessary. 368 embedder_web_contents->GetRenderProcessHost()->RemoveObserver(this); 369 embedder_web_contents_ = embedder_web_contents; 370 embedder_web_contents_observer_.reset( 371 new EmbedderWebContentsObserver(this)); 372 element_instance_id_ = element_instance_id; 373 374 WillAttachToEmbedder(); 375} 376 377void GuestViewBase::DidStopLoading(content::RenderViewHost* render_view_host) { 378 if (!IsDragAndDropEnabled()) { 379 const char script[] = "window.addEventListener('dragstart', function() { " 380 " window.event.preventDefault(); " 381 "});"; 382 render_view_host->GetMainFrame()->ExecuteJavaScript( 383 base::ASCIIToUTF16(script)); 384 } 385 DidStopLoading(); 386} 387 388void GuestViewBase::RenderViewReady() { 389 GuestReady(); 390} 391 392void GuestViewBase::WebContentsDestroyed() { 393 GuestDestroyed(); 394 delete this; 395} 396 397void GuestViewBase::ActivateContents(WebContents* web_contents) { 398 if (!attached() || !embedder_web_contents()->GetDelegate()) 399 return; 400 401 embedder_web_contents()->GetDelegate()->ActivateContents( 402 embedder_web_contents()); 403} 404 405void GuestViewBase::DeactivateContents(WebContents* web_contents) { 406 if (!attached() || !embedder_web_contents()->GetDelegate()) 407 return; 408 409 embedder_web_contents()->GetDelegate()->DeactivateContents( 410 embedder_web_contents()); 411} 412 413void GuestViewBase::RunFileChooser(WebContents* web_contents, 414 const content::FileChooserParams& params) { 415 if (!attached() || !embedder_web_contents()->GetDelegate()) 416 return; 417 418 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params); 419} 420 421bool GuestViewBase::ShouldFocusPageAfterCrash() { 422 // Focus is managed elsewhere. 423 return false; 424} 425 426bool GuestViewBase::PreHandleGestureEvent(content::WebContents* source, 427 const blink::WebGestureEvent& event) { 428 return event.type == blink::WebGestureEvent::GesturePinchBegin || 429 event.type == blink::WebGestureEvent::GesturePinchUpdate || 430 event.type == blink::WebGestureEvent::GesturePinchEnd; 431} 432 433GuestViewBase::~GuestViewBase() { 434} 435 436void GuestViewBase::DispatchEventToEmbedder(Event* event) { 437 scoped_ptr<Event> event_ptr(event); 438 439 if (!attached()) { 440 pending_events_.push_back(linked_ptr<Event>(event_ptr.release())); 441 return; 442 } 443 444 EventFilteringInfo info; 445 info.SetInstanceID(view_instance_id_); 446 scoped_ptr<base::ListValue> args(new base::ListValue()); 447 args->Append(event->GetArguments().release()); 448 449 EventRouter::DispatchEvent( 450 embedder_web_contents_, 451 browser_context_, 452 embedder_extension_id_, 453 event->name(), 454 args.Pass(), 455 EventRouter::USER_GESTURE_UNKNOWN, 456 info); 457} 458 459void GuestViewBase::SendQueuedEvents() { 460 if (!attached()) 461 return; 462 while (!pending_events_.empty()) { 463 linked_ptr<Event> event_ptr = pending_events_.front(); 464 pending_events_.pop_front(); 465 DispatchEventToEmbedder(event_ptr.release()); 466 } 467} 468 469void GuestViewBase::CompleteInit(const std::string& embedder_extension_id, 470 int embedder_render_process_id, 471 const WebContentsCreatedCallback& callback, 472 content::WebContents* guest_web_contents) { 473 if (!guest_web_contents) { 474 // The derived class did not create a WebContents so this class serves no 475 // purpose. Let's self-destruct. 476 delete this; 477 callback.Run(NULL); 478 return; 479 } 480 InitWithWebContents(embedder_extension_id, 481 embedder_render_process_id, 482 guest_web_contents); 483 callback.Run(guest_web_contents); 484} 485 486// static 487void GuestViewBase::RegisterGuestViewTypes() { 488 AppViewGuest::Register(); 489 ExtensionOptionsGuest::Register(); 490 MimeHandlerViewGuest::Register(); 491 WebViewGuest::Register(); 492} 493 494} // namespace extensions 495