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