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
;
36 struct FrameNavigateParams
;
39 namespace extensions
{
43 typedef std::map
<std::string
, GuestViewBase::GuestCreationCallback
>
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
;
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() {
66 // This observer ensures that the GuestViewBase destroys itself when its
67 // embedder goes away.
68 class GuestViewBase::OwnerLifetimeObserver
: public WebContentsObserver
{
70 OwnerLifetimeObserver(GuestViewBase
* guest
,
71 content::WebContents
* embedder_web_contents
)
72 : WebContentsObserver(embedder_web_contents
),
76 ~OwnerLifetimeObserver() override
{}
78 // WebContentsObserver implementation.
79 void WebContentsDestroyed() override
{
80 // If the embedder is destroyed then destroy the guest.
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())
92 void RenderProcessGone(base::TerminationStatus status
) override
{
93 // If the embedder crashes, then destroy the guest.
99 GuestViewBase
* guest_
;
106 guest_
->EmbedderWillBeDestroyed();
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
{
117 OpenerLifetimeObserver(GuestViewBase
* guest
)
118 : WebContentsObserver(guest
->GetOpener()->web_contents()),
121 ~OpenerLifetimeObserver() override
{}
123 // WebContentsObserver implementation.
124 void WebContentsDestroyed() override
{
125 if (guest_
->attached())
128 // If the opener is destroyed then destroy the guest.
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
),
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
) {
160 const Feature
* feature
= FeatureProvider::GetAPIFeature(GetAPINamespace());
163 ProcessMap
* process_map
= ProcessMap::Get(browser_context());
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(
173 process_map
->GetMostLikelyContextType(
175 owner_web_contents()->GetRenderProcessHost()->GetID()),
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.
185 CreateWebContents(create_params
,
186 base::Bind(&GuestViewBase::CompleteInit
,
187 weak_ptr_factory_
.GetWeakPtr(),
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.
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_
)
232 auto_size_enabled_
= enabled
;
237 content::RenderViewHost
* rvh
= web_contents()->GetRenderViewHost();
238 if (auto_size_enabled_
) {
239 rvh
->EnableAutoResize(min_auto_size_
, max_auto_size_
);
241 rvh
->DisableAutoResize(element_size_
);
242 guest_size_
= element_size_
;
243 GuestSizeChangedDueToAutoSize(guest_size_
, element_size_
);
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
;
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()) {
272 return it
->second
.Run(browser_context
, owner_web_contents
, guest_instance_id
);
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
;
283 GuestViewBase
* GuestViewBase::From(int embedder_process_id
,
284 int guest_instance_id
) {
285 content::RenderProcessHost
* host
=
286 content::RenderProcessHost::FromID(embedder_process_id
);
290 content::WebContents
* guest_web_contents
=
291 GuestViewManager::FromBrowserContext(host
->GetBrowserContext())->
292 GetGuestByInstanceIDSafely(guest_instance_id
, embedder_process_id
);
293 if (!guest_web_contents
)
296 return GuestViewBase::FromWebContents(guest_web_contents
);
300 bool GuestViewBase::IsGuest(WebContents
* web_contents
) {
301 return !!GuestViewBase::FromWebContents(web_contents
);
304 bool GuestViewBase::IsAutoSizeSupported() const {
308 bool GuestViewBase::IsDragAndDropEnabled() const {
312 bool GuestViewBase::ZoomPropagatesFromEmbedderToGuest() const {
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
));
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_
)
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_
)
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.
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
,
400 void GuestViewBase::SetOpener(GuestViewBase
* guest
) {
401 if (guest
&& guest
->IsViewType(GetViewType())) {
402 opener_
= guest
->weak_ptr_factory_
.GetWeakPtr();
404 opener_lifetime_observer_
.reset(new OpenerLifetimeObserver(this));
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(); "
441 render_view_host
->GetMainFrame()->ExecuteJavaScript(
442 base::ASCIIToUTF16(script
));
447 void GuestViewBase::RenderViewReady() {
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
463 void GuestViewBase::ActivateContents(WebContents
* web_contents
) {
464 if (!attached() || !embedder_web_contents()->GetDelegate())
467 embedder_web_contents()->GetDelegate()->ActivateContents(
468 embedder_web_contents());
471 void GuestViewBase::DeactivateContents(WebContents
* web_contents
) {
472 if (!attached() || !embedder_web_contents()->GetDelegate())
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())
484 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents
, params
);
487 bool GuestViewBase::ShouldFocusPageAfterCrash() {
488 // Focus is managed elsewhere.
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
))
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
);
516 pending_events_
.push_back(linked_ptr
<Event
>(event_ptr
.release()));
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(
531 EventRouter::USER_GESTURE_UNKNOWN
,
535 void GuestViewBase::SendQueuedEvents() {
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.
555 InitWithWebContents(owner_extension_id
, guest_web_contents
);
556 callback
.Run(guest_web_contents
);
559 void GuestViewBase::StartTrackingEmbedderZoomLevel() {
560 if (!ZoomPropagatesFromEmbedderToGuest())
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
)
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())
579 ui_zoom::ZoomController
* zoom_controller
=
580 ui_zoom::ZoomController::FromWebContents(owner_web_contents());
581 if (!zoom_controller
)
583 zoom_controller
->RemoveObserver(this);
587 void GuestViewBase::RegisterGuestViewTypes() {
588 AppViewGuest::Register();
589 ExtensionOptionsGuest::Register();
590 MimeHandlerViewGuest::Register();
591 SurfaceWorkerGuest::Register();
592 WebViewGuest::Register();
595 } // namespace extensions