Chromecast on Android buildfix: don't try to create a RendererFactory.
[chromium-blink-merge.git] / extensions / browser / guest_view / guest_view_base.cc
blobde7e1d8443547a353d2d96521d5f0dbdd042167f
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.
5 #include "extensions/browser/guest_view/guest_view_base.h"
7 #include "base/lazy_instance.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "components/ui/zoom/zoom_controller.h"
10 #include "content/public/browser/navigation_details.h"
11 #include "content/public/browser/render_frame_host.h"
12 #include "content/public/browser/render_process_host.h"
13 #include "content/public/browser/render_view_host.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/public/common/page_zoom.h"
16 #include "content/public/common/url_constants.h"
17 #include "extensions/browser/api/extensions_api_client.h"
18 #include "extensions/browser/event_router.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/guest_view/app_view/app_view_guest.h"
21 #include "extensions/browser/guest_view/extension_options/extension_options_guest.h"
22 #include "extensions/browser/guest_view/guest_view_manager.h"
23 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
24 #include "extensions/browser/guest_view/surface_worker/surface_worker_guest.h"
25 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
26 #include "extensions/browser/process_map.h"
27 #include "extensions/common/extension_messages.h"
28 #include "extensions/common/features/feature.h"
29 #include "extensions/common/features/feature_provider.h"
30 #include "extensions/common/guest_view/guest_view_constants.h"
31 #include "third_party/WebKit/public/web/WebInputEvent.h"
33 using content::WebContents;
35 namespace content {
36 struct FrameNavigateParams;
39 namespace extensions {
41 namespace {
43 typedef std::map<std::string, GuestViewBase::GuestCreationCallback>
44 GuestViewCreationMap;
45 static base::LazyInstance<GuestViewCreationMap> guest_view_registry =
46 LAZY_INSTANCE_INITIALIZER;
48 typedef std::map<WebContents*, GuestViewBase*> WebContentsGuestViewMap;
49 static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map =
50 LAZY_INSTANCE_INITIALIZER;
52 } // namespace
54 GuestViewBase::Event::Event(const std::string& name,
55 scoped_ptr<base::DictionaryValue> args)
56 : name_(name), args_(args.Pass()) {
59 GuestViewBase::Event::~Event() {
62 scoped_ptr<base::DictionaryValue> GuestViewBase::Event::GetArguments() {
63 return args_.Pass();
66 // This observer ensures that the GuestViewBase destroys itself when its
67 // embedder goes away.
68 class GuestViewBase::OwnerLifetimeObserver : public WebContentsObserver {
69 public:
70 OwnerLifetimeObserver(GuestViewBase* guest,
71 content::WebContents* embedder_web_contents)
72 : WebContentsObserver(embedder_web_contents),
73 destroyed_(false),
74 guest_(guest) {}
76 ~OwnerLifetimeObserver() override {}
78 // WebContentsObserver implementation.
79 void WebContentsDestroyed() override {
80 // If the embedder is destroyed then destroy the guest.
81 Destroy();
84 void DidNavigateMainFrame(
85 const content::LoadCommittedDetails& details,
86 const content::FrameNavigateParams& params) override {
87 // If the embedder navigates to a different page then destroy the guest.
88 if (details.is_navigation_to_different_page())
89 Destroy();
92 void RenderProcessGone(base::TerminationStatus status) override {
93 // If the embedder crashes, then destroy the guest.
94 Destroy();
97 private:
98 bool destroyed_;
99 GuestViewBase* guest_;
101 void Destroy() {
102 if (destroyed_)
103 return;
105 destroyed_ = true;
106 guest_->EmbedderWillBeDestroyed();
107 guest_->Destroy();
110 DISALLOW_COPY_AND_ASSIGN(OwnerLifetimeObserver);
113 // This observer ensures that the GuestViewBase destroys itself when its
114 // embedder goes away.
115 class GuestViewBase::OpenerLifetimeObserver : public WebContentsObserver {
116 public:
117 OpenerLifetimeObserver(GuestViewBase* guest)
118 : WebContentsObserver(guest->GetOpener()->web_contents()),
119 guest_(guest) {}
121 ~OpenerLifetimeObserver() override {}
123 // WebContentsObserver implementation.
124 void WebContentsDestroyed() override {
125 if (guest_->attached())
126 return;
128 // If the opener is destroyed then destroy the guest.
129 guest_->Destroy();
132 private:
133 GuestViewBase* guest_;
135 DISALLOW_COPY_AND_ASSIGN(OpenerLifetimeObserver);
138 GuestViewBase::GuestViewBase(content::BrowserContext* browser_context,
139 content::WebContents* owner_web_contents,
140 int guest_instance_id)
141 : owner_web_contents_(owner_web_contents),
142 browser_context_(browser_context),
143 guest_instance_id_(guest_instance_id),
144 view_instance_id_(guestview::kInstanceIDNone),
145 element_instance_id_(guestview::kInstanceIDNone),
146 initialized_(false),
147 is_being_destroyed_(false),
148 auto_size_enabled_(false),
149 is_full_page_plugin_(false),
150 weak_ptr_factory_(this) {
153 void GuestViewBase::Init(const std::string& owner_extension_id,
154 const base::DictionaryValue& create_params,
155 const WebContentsCreatedCallback& callback) {
156 if (initialized_)
157 return;
158 initialized_ = true;
160 const Feature* feature = FeatureProvider::GetAPIFeature(GetAPINamespace());
161 CHECK(feature);
163 ProcessMap* process_map = ProcessMap::Get(browser_context());
164 CHECK(process_map);
166 const Extension* embedder_extension = ExtensionRegistry::Get(browser_context_)
167 ->enabled_extensions()
168 .GetByID(owner_extension_id);
170 // Ok for |embedder_extension| to be NULL, the embedder might be WebUI.
171 Feature::Availability availability = feature->IsAvailableToContext(
172 embedder_extension,
173 process_map->GetMostLikelyContextType(
174 embedder_extension,
175 owner_web_contents()->GetRenderProcessHost()->GetID()),
176 GetOwnerSiteURL());
177 if (!availability.is_available()) {
178 // The derived class did not create a WebContents so this class serves no
179 // purpose. Let's self-destruct.
180 delete this;
181 callback.Run(NULL);
182 return;
185 CreateWebContents(create_params,
186 base::Bind(&GuestViewBase::CompleteInit,
187 weak_ptr_factory_.GetWeakPtr(),
188 owner_extension_id,
189 callback));
192 void GuestViewBase::InitWithWebContents(
193 const std::string& owner_extension_id,
194 content::WebContents* guest_web_contents) {
195 DCHECK(guest_web_contents);
197 owner_extension_id_ = owner_extension_id;
199 // At this point, we have just created the guest WebContents, we need to add
200 // an observer to the embedder WebContents. This observer will be responsible
201 // for destroying the guest WebContents if the embedder goes away.
202 owner_lifetime_observer_.reset(
203 new OwnerLifetimeObserver(this, owner_web_contents_));
205 WebContentsObserver::Observe(guest_web_contents);
206 guest_web_contents->SetDelegate(this);
207 webcontents_guestview_map.Get().insert(
208 std::make_pair(guest_web_contents, this));
209 GuestViewManager::FromBrowserContext(browser_context_)->
210 AddGuest(guest_instance_id_, guest_web_contents);
212 // Create a ZoomController to allow the guest's contents to be zoomed.
213 ui_zoom::ZoomController::CreateForWebContents(guest_web_contents);
215 // Give the derived class an opportunity to perform additional initialization.
216 DidInitialize();
219 void GuestViewBase::SetAutoSize(bool enabled,
220 const gfx::Size& min_size,
221 const gfx::Size& max_size) {
222 min_auto_size_ = min_size;
223 min_auto_size_.SetToMin(max_size);
224 max_auto_size_ = max_size;
225 max_auto_size_.SetToMax(min_size);
227 enabled &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty() &&
228 IsAutoSizeSupported();
229 if (!enabled && !auto_size_enabled_)
230 return;
232 auto_size_enabled_ = enabled;
234 if (!attached())
235 return;
237 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
238 if (auto_size_enabled_) {
239 rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
240 } else {
241 rvh->DisableAutoResize(element_size_);
242 guest_size_ = element_size_;
243 GuestSizeChangedDueToAutoSize(guest_size_, element_size_);
247 // static
248 void GuestViewBase::RegisterGuestViewType(
249 const std::string& view_type,
250 const GuestCreationCallback& callback) {
251 GuestViewCreationMap::iterator it =
252 guest_view_registry.Get().find(view_type);
253 DCHECK(it == guest_view_registry.Get().end());
254 guest_view_registry.Get()[view_type] = callback;
257 // static
258 GuestViewBase* GuestViewBase::Create(
259 content::BrowserContext* browser_context,
260 content::WebContents* owner_web_contents,
261 int guest_instance_id,
262 const std::string& view_type) {
263 if (guest_view_registry.Get().empty())
264 RegisterGuestViewTypes();
266 GuestViewCreationMap::iterator it =
267 guest_view_registry.Get().find(view_type);
268 if (it == guest_view_registry.Get().end()) {
269 NOTREACHED();
270 return NULL;
272 return it->second.Run(browser_context, owner_web_contents, guest_instance_id);
275 // static
276 GuestViewBase* GuestViewBase::FromWebContents(WebContents* web_contents) {
277 WebContentsGuestViewMap* guest_map = webcontents_guestview_map.Pointer();
278 WebContentsGuestViewMap::iterator it = guest_map->find(web_contents);
279 return it == guest_map->end() ? NULL : it->second;
282 // static
283 GuestViewBase* GuestViewBase::From(int embedder_process_id,
284 int guest_instance_id) {
285 content::RenderProcessHost* host =
286 content::RenderProcessHost::FromID(embedder_process_id);
287 if (!host)
288 return NULL;
290 content::WebContents* guest_web_contents =
291 GuestViewManager::FromBrowserContext(host->GetBrowserContext())->
292 GetGuestByInstanceIDSafely(guest_instance_id, embedder_process_id);
293 if (!guest_web_contents)
294 return NULL;
296 return GuestViewBase::FromWebContents(guest_web_contents);
299 // static
300 bool GuestViewBase::IsGuest(WebContents* web_contents) {
301 return !!GuestViewBase::FromWebContents(web_contents);
304 bool GuestViewBase::IsAutoSizeSupported() const {
305 return false;
308 bool GuestViewBase::IsDragAndDropEnabled() const {
309 return false;
312 bool GuestViewBase::ZoomPropagatesFromEmbedderToGuest() const {
313 return true;
316 void GuestViewBase::DidAttach(int guest_proxy_routing_id) {
317 opener_lifetime_observer_.reset();
319 // Give the derived class an opportunity to perform some actions.
320 DidAttachToEmbedder();
322 // Inform the associated GuestViewContainer that the contentWindow is ready.
323 embedder_web_contents()->Send(new ExtensionMsg_GuestAttached(
324 element_instance_id_,
325 guest_proxy_routing_id));
327 SendQueuedEvents();
330 void GuestViewBase::DidDetach() {
331 GuestViewManager::FromBrowserContext(browser_context_)->DetachGuest(
332 this, element_instance_id_);
333 StopTrackingEmbedderZoomLevel();
334 owner_web_contents()->Send(new ExtensionMsg_GuestDetached(
335 element_instance_id_));
336 element_instance_id_ = guestview::kInstanceIDNone;
339 void GuestViewBase::ElementSizeChanged(const gfx::Size& old_size,
340 const gfx::Size& new_size) {
341 element_size_ = new_size;
344 WebContents* GuestViewBase::GetOwnerWebContents() const {
345 return owner_web_contents_;
348 void GuestViewBase::GuestSizeChanged(const gfx::Size& old_size,
349 const gfx::Size& new_size) {
350 if (!auto_size_enabled_)
351 return;
352 guest_size_ = new_size;
353 GuestSizeChangedDueToAutoSize(old_size, new_size);
356 const GURL& GuestViewBase::GetOwnerSiteURL() const {
357 return owner_web_contents()->GetLastCommittedURL();
360 void GuestViewBase::Destroy() {
361 if (is_being_destroyed_)
362 return;
364 is_being_destroyed_ = true;
366 // It is important to clear owner_web_contents_ after the call to
367 // StopTrackingEmbedderZoomLevel(), but before the rest of
368 // the statements in this function.
369 StopTrackingEmbedderZoomLevel();
370 owner_web_contents_ = NULL;
372 DCHECK(web_contents());
374 // Give the derived class an opportunity to perform some cleanup.
375 WillDestroy();
377 // Invalidate weak pointers now so that bound callbacks cannot be called late
378 // into destruction. We must call this after WillDestroy because derived types
379 // may wish to access their openers.
380 weak_ptr_factory_.InvalidateWeakPtrs();
382 // Give the content module an opportunity to perform some cleanup.
383 if (!destruction_callback_.is_null())
384 destruction_callback_.Run();
386 webcontents_guestview_map.Get().erase(web_contents());
387 GuestViewManager::FromBrowserContext(browser_context_)->
388 RemoveGuest(guest_instance_id_);
389 pending_events_.clear();
391 delete web_contents();
394 void GuestViewBase::SetAttachParams(const base::DictionaryValue& params) {
395 attach_params_.reset(params.DeepCopy());
396 attach_params_->GetInteger(guestview::kParameterInstanceId,
397 &view_instance_id_);
400 void GuestViewBase::SetOpener(GuestViewBase* guest) {
401 if (guest && guest->IsViewType(GetViewType())) {
402 opener_ = guest->weak_ptr_factory_.GetWeakPtr();
403 if (!attached())
404 opener_lifetime_observer_.reset(new OpenerLifetimeObserver(this));
405 return;
407 opener_ = base::WeakPtr<GuestViewBase>();
408 opener_lifetime_observer_.reset();
411 void GuestViewBase::RegisterDestructionCallback(
412 const DestructionCallback& callback) {
413 destruction_callback_ = callback;
416 void GuestViewBase::WillAttach(content::WebContents* embedder_web_contents,
417 int element_instance_id,
418 bool is_full_page_plugin) {
419 if (owner_web_contents_ != embedder_web_contents) {
420 DCHECK_EQ(owner_lifetime_observer_->web_contents(), owner_web_contents_);
421 // Stop tracking the old embedder's zoom level.
422 StopTrackingEmbedderZoomLevel();
423 owner_web_contents_ = embedder_web_contents;
424 owner_lifetime_observer_.reset(
425 new OwnerLifetimeObserver(this, embedder_web_contents));
428 // Start tracking the new embedder's zoom level.
429 StartTrackingEmbedderZoomLevel();
430 element_instance_id_ = element_instance_id;
431 is_full_page_plugin_ = is_full_page_plugin;
433 WillAttachToEmbedder();
436 void GuestViewBase::DidStopLoading(content::RenderViewHost* render_view_host) {
437 if (!IsDragAndDropEnabled()) {
438 const char script[] = "window.addEventListener('dragstart', function() { "
439 " window.event.preventDefault(); "
440 "});";
441 render_view_host->GetMainFrame()->ExecuteJavaScript(
442 base::ASCIIToUTF16(script));
444 DidStopLoading();
447 void GuestViewBase::RenderViewReady() {
448 GuestReady();
451 void GuestViewBase::WebContentsDestroyed() {
452 // Let the derived class know that its WebContents is in the process of
453 // being destroyed. web_contents() is still valid at this point.
454 // TODO(fsamuel): This allows for reentrant code into WebContents during
455 // destruction. This could potentially lead to bugs. Perhaps we should get rid
456 // of this?
457 GuestDestroyed();
459 // Self-destruct.
460 delete this;
463 void GuestViewBase::ActivateContents(WebContents* web_contents) {
464 if (!attached() || !embedder_web_contents()->GetDelegate())
465 return;
467 embedder_web_contents()->GetDelegate()->ActivateContents(
468 embedder_web_contents());
471 void GuestViewBase::DeactivateContents(WebContents* web_contents) {
472 if (!attached() || !embedder_web_contents()->GetDelegate())
473 return;
475 embedder_web_contents()->GetDelegate()->DeactivateContents(
476 embedder_web_contents());
479 void GuestViewBase::RunFileChooser(WebContents* web_contents,
480 const content::FileChooserParams& params) {
481 if (!attached() || !embedder_web_contents()->GetDelegate())
482 return;
484 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params);
487 bool GuestViewBase::ShouldFocusPageAfterCrash() {
488 // Focus is managed elsewhere.
489 return false;
492 bool GuestViewBase::PreHandleGestureEvent(content::WebContents* source,
493 const blink::WebGestureEvent& event) {
494 return event.type == blink::WebGestureEvent::GesturePinchBegin ||
495 event.type == blink::WebGestureEvent::GesturePinchUpdate ||
496 event.type == blink::WebGestureEvent::GesturePinchEnd;
499 GuestViewBase::~GuestViewBase() {
502 void GuestViewBase::OnZoomChanged(
503 const ui_zoom::ZoomController::ZoomChangedEventData& data) {
504 if (content::ZoomValuesEqual(data.old_zoom_level, data.new_zoom_level))
505 return;
506 // When the embedder's zoom level is changed, then we also update the
507 // guest's zoom level to match.
508 ui_zoom::ZoomController::FromWebContents(web_contents())
509 ->SetZoomLevel(data.new_zoom_level);
512 void GuestViewBase::DispatchEventToEmbedder(Event* event) {
513 scoped_ptr<Event> event_ptr(event);
515 if (!attached()) {
516 pending_events_.push_back(linked_ptr<Event>(event_ptr.release()));
517 return;
520 EventFilteringInfo info;
521 info.SetInstanceID(view_instance_id_);
522 scoped_ptr<base::ListValue> args(new base::ListValue());
523 args->Append(event->GetArguments().release());
525 EventRouter::DispatchEvent(
526 owner_web_contents_,
527 browser_context_,
528 owner_extension_id_,
529 event->name(),
530 args.Pass(),
531 EventRouter::USER_GESTURE_UNKNOWN,
532 info);
535 void GuestViewBase::SendQueuedEvents() {
536 if (!attached())
537 return;
538 while (!pending_events_.empty()) {
539 linked_ptr<Event> event_ptr = pending_events_.front();
540 pending_events_.pop_front();
541 DispatchEventToEmbedder(event_ptr.release());
545 void GuestViewBase::CompleteInit(const std::string& owner_extension_id,
546 const WebContentsCreatedCallback& callback,
547 content::WebContents* guest_web_contents) {
548 if (!guest_web_contents) {
549 // The derived class did not create a WebContents so this class serves no
550 // purpose. Let's self-destruct.
551 delete this;
552 callback.Run(NULL);
553 return;
555 InitWithWebContents(owner_extension_id, guest_web_contents);
556 callback.Run(guest_web_contents);
559 void GuestViewBase::StartTrackingEmbedderZoomLevel() {
560 if (!ZoomPropagatesFromEmbedderToGuest())
561 return;
563 ui_zoom::ZoomController* zoom_controller =
564 ui_zoom::ZoomController::FromWebContents(owner_web_contents());
565 // Chrome Apps do not have a ZoomController.
566 if (!zoom_controller)
567 return;
568 // Listen to the embedder's zoom changes.
569 zoom_controller->AddObserver(this);
570 // Set the guest's initial zoom level to be equal to the embedder's.
571 ui_zoom::ZoomController::FromWebContents(web_contents())
572 ->SetZoomLevel(zoom_controller->GetZoomLevel());
575 void GuestViewBase::StopTrackingEmbedderZoomLevel() {
576 if (!attached() || !ZoomPropagatesFromEmbedderToGuest())
577 return;
579 ui_zoom::ZoomController* zoom_controller =
580 ui_zoom::ZoomController::FromWebContents(owner_web_contents());
581 if (!zoom_controller)
582 return;
583 zoom_controller->RemoveObserver(this);
586 // static
587 void GuestViewBase::RegisterGuestViewTypes() {
588 AppViewGuest::Register();
589 ExtensionOptionsGuest::Register();
590 MimeHandlerViewGuest::Register();
591 SurfaceWorkerGuest::Register();
592 WebViewGuest::Register();
595 } // namespace extensions