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