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 "content/public/browser/navigation_details.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/app_view/app_view_guest.h"
19 #include "extensions/browser/guest_view/extension_options/extension_options_guest.h"
20 #include "extensions/browser/guest_view/guest_view_manager.h"
21 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
22 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
23 #include "extensions/browser/process_map.h"
24 #include "extensions/common/extension_messages.h"
25 #include "extensions/common/features/feature.h"
26 #include "extensions/common/features/feature_provider.h"
27 #include "extensions/common/guest_view/guest_view_constants.h"
28 #include "third_party/WebKit/public/web/WebInputEvent.h"
30 using content::WebContents
;
33 struct FrameNavigateParams
;
36 namespace extensions
{
40 typedef std::map
<std::string
, GuestViewBase::GuestCreationCallback
>
42 static base::LazyInstance
<GuestViewCreationMap
> guest_view_registry
=
43 LAZY_INSTANCE_INITIALIZER
;
45 typedef std::map
<WebContents
*, GuestViewBase
*> WebContentsGuestViewMap
;
46 static base::LazyInstance
<WebContentsGuestViewMap
> webcontents_guestview_map
=
47 LAZY_INSTANCE_INITIALIZER
;
51 GuestViewBase::Event::Event(const std::string
& name
,
52 scoped_ptr
<base::DictionaryValue
> args
)
53 : name_(name
), args_(args
.Pass()) {
56 GuestViewBase::Event::~Event() {
59 scoped_ptr
<base::DictionaryValue
> GuestViewBase::Event::GetArguments() {
63 // This observer ensures that the GuestViewBase destroys itself when its
64 // embedder goes away.
65 class GuestViewBase::EmbedderLifetimeObserver
: public WebContentsObserver
{
67 EmbedderLifetimeObserver(GuestViewBase
* guest
,
68 content::WebContents
* embedder_web_contents
)
69 : WebContentsObserver(embedder_web_contents
),
73 ~EmbedderLifetimeObserver() override
{}
75 // WebContentsObserver implementation.
76 void WebContentsDestroyed() override
{
77 // If the embedder is destroyed then destroy the guest.
81 void DidNavigateMainFrame(
82 const content::LoadCommittedDetails
& details
,
83 const content::FrameNavigateParams
& params
) override
{
84 // If the embedder navigates to a different page then destroy the guest.
85 if (details
.is_navigation_to_different_page())
89 void RenderProcessGone(base::TerminationStatus status
) override
{
90 // If the embedder crashes, then destroy the guest.
96 GuestViewBase
* guest_
;
103 guest_
->EmbedderWillBeDestroyed();
104 guest_
->embedder_web_contents_
= NULL
;
108 DISALLOW_COPY_AND_ASSIGN(EmbedderLifetimeObserver
);
111 // This observer ensures that the GuestViewBase destroys itself when its
112 // embedder goes away.
113 class GuestViewBase::OpenerLifetimeObserver
: public WebContentsObserver
{
115 OpenerLifetimeObserver(GuestViewBase
* guest
)
116 : WebContentsObserver(guest
->GetOpener()->web_contents()),
119 ~OpenerLifetimeObserver() override
{}
121 // WebContentsObserver implementation.
122 void WebContentsDestroyed() override
{
123 if (guest_
->attached())
126 // If the opener is destroyed then destroy the guest.
131 GuestViewBase
* guest_
;
133 DISALLOW_COPY_AND_ASSIGN(OpenerLifetimeObserver
);
136 GuestViewBase::GuestViewBase(content::BrowserContext
* browser_context
,
137 int guest_instance_id
)
138 : embedder_web_contents_(NULL
),
139 embedder_render_process_id_(0),
140 browser_context_(browser_context
),
141 guest_instance_id_(guest_instance_id
),
142 view_instance_id_(guestview::kInstanceIDNone
),
143 element_instance_id_(guestview::kInstanceIDNone
),
145 is_being_destroyed_(false),
146 auto_size_enabled_(false),
147 weak_ptr_factory_(this) {
150 void GuestViewBase::Init(const std::string
& embedder_extension_id
,
151 content::WebContents
* embedder_web_contents
,
152 const base::DictionaryValue
& create_params
,
153 const WebContentsCreatedCallback
& callback
) {
158 Feature
* feature
= FeatureProvider::GetAPIFeatures()->GetFeature(
162 ProcessMap
* process_map
= ProcessMap::Get(browser_context());
165 const Extension
* embedder_extension
= ExtensionRegistry::Get(browser_context_
)
166 ->enabled_extensions()
167 .GetByID(embedder_extension_id
);
168 // Ok for |embedder_extension| to be NULL, the embedder might be WebUI.
170 CHECK(embedder_web_contents
);
171 int embedder_process_id
=
172 embedder_web_contents
->GetRenderProcessHost()->GetID();
174 const GURL
& embedder_site_url
= embedder_web_contents
->GetLastCommittedURL();
175 Feature::Availability availability
= feature
->IsAvailableToContext(
177 process_map
->GetMostLikelyContextType(embedder_extension
,
178 embedder_process_id
),
180 if (!availability
.is_available()) {
181 // The derived class did not create a WebContents so this class serves no
182 // purpose. Let's self-destruct.
188 CreateWebContents(embedder_extension_id
,
192 base::Bind(&GuestViewBase::CompleteInit
,
193 weak_ptr_factory_
.GetWeakPtr(),
194 embedder_extension_id
,
195 embedder_web_contents
,
199 void GuestViewBase::InitWithWebContents(
200 const std::string
& embedder_extension_id
,
201 content::WebContents
* embedder_web_contents
,
202 content::WebContents
* guest_web_contents
) {
203 DCHECK(guest_web_contents
);
204 DCHECK(embedder_web_contents
);
205 int embedder_render_process_id
=
206 embedder_web_contents
->GetRenderProcessHost()->GetID();
207 content::RenderProcessHost
* embedder_render_process_host
=
208 content::RenderProcessHost::FromID(embedder_render_process_id
);
210 embedder_extension_id_
= embedder_extension_id
;
211 embedder_render_process_id_
= embedder_render_process_host
->GetID();
213 // At this point, we have just created the guest WebContents, we need to add
214 // an observer to the embedder WebContents. This observer will be responsible
215 // for destroying the guest WebContents if the embedder goes away.
216 embedder_lifetime_observer_
.reset(
217 new EmbedderLifetimeObserver(this, embedder_web_contents
));
219 WebContentsObserver::Observe(guest_web_contents
);
220 guest_web_contents
->SetDelegate(this);
221 webcontents_guestview_map
.Get().insert(
222 std::make_pair(guest_web_contents
, this));
223 GuestViewManager::FromBrowserContext(browser_context_
)->
224 AddGuest(guest_instance_id_
, guest_web_contents
);
226 // Give the derived class an opportunity to perform additional initialization.
230 void GuestViewBase::SetAutoSize(bool enabled
,
231 const gfx::Size
& min_size
,
232 const gfx::Size
& max_size
) {
233 min_auto_size_
= min_size
;
234 min_auto_size_
.SetToMin(max_size
);
235 max_auto_size_
= max_size
;
236 max_auto_size_
.SetToMax(min_size
);
238 enabled
&= !min_auto_size_
.IsEmpty() && !max_auto_size_
.IsEmpty() &&
239 IsAutoSizeSupported();
240 if (!enabled
&& !auto_size_enabled_
)
243 auto_size_enabled_
= enabled
;
248 content::RenderViewHost
* rvh
= web_contents()->GetRenderViewHost();
249 if (auto_size_enabled_
) {
250 rvh
->EnableAutoResize(min_auto_size_
, max_auto_size_
);
252 rvh
->DisableAutoResize(element_size_
);
253 guest_size_
= element_size_
;
254 GuestSizeChangedDueToAutoSize(guest_size_
, element_size_
);
259 void GuestViewBase::RegisterGuestViewType(
260 const std::string
& view_type
,
261 const GuestCreationCallback
& callback
) {
262 GuestViewCreationMap::iterator it
=
263 guest_view_registry
.Get().find(view_type
);
264 DCHECK(it
== guest_view_registry
.Get().end());
265 guest_view_registry
.Get()[view_type
] = callback
;
269 GuestViewBase
* GuestViewBase::Create(
270 content::BrowserContext
* browser_context
,
271 int guest_instance_id
,
272 const std::string
& view_type
) {
273 if (guest_view_registry
.Get().empty())
274 RegisterGuestViewTypes();
276 GuestViewCreationMap::iterator it
=
277 guest_view_registry
.Get().find(view_type
);
278 if (it
== guest_view_registry
.Get().end()) {
282 return it
->second
.Run(browser_context
, guest_instance_id
);
286 GuestViewBase
* GuestViewBase::FromWebContents(WebContents
* web_contents
) {
287 WebContentsGuestViewMap
* guest_map
= webcontents_guestview_map
.Pointer();
288 WebContentsGuestViewMap::iterator it
= guest_map
->find(web_contents
);
289 return it
== guest_map
->end() ? NULL
: it
->second
;
293 GuestViewBase
* GuestViewBase::From(int embedder_process_id
,
294 int guest_instance_id
) {
295 content::RenderProcessHost
* host
=
296 content::RenderProcessHost::FromID(embedder_process_id
);
300 content::WebContents
* guest_web_contents
=
301 GuestViewManager::FromBrowserContext(host
->GetBrowserContext())->
302 GetGuestByInstanceIDSafely(guest_instance_id
, embedder_process_id
);
303 if (!guest_web_contents
)
306 return GuestViewBase::FromWebContents(guest_web_contents
);
310 bool GuestViewBase::IsGuest(WebContents
* web_contents
) {
311 return !!GuestViewBase::FromWebContents(web_contents
);
314 bool GuestViewBase::IsAutoSizeSupported() const {
318 bool GuestViewBase::IsDragAndDropEnabled() const {
322 void GuestViewBase::DidAttach(int guest_proxy_routing_id
) {
323 opener_lifetime_observer_
.reset();
325 // Give the derived class an opportunity to perform some actions.
326 DidAttachToEmbedder();
328 // Inform the associated GuestViewContainer that the contentWindow is ready.
329 embedder_web_contents()->Send(new ExtensionMsg_GuestAttached(
330 embedder_web_contents()->GetMainFrame()->GetRoutingID(),
331 element_instance_id_
,
332 guest_proxy_routing_id
));
337 void GuestViewBase::ElementSizeChanged(const gfx::Size
& old_size
,
338 const gfx::Size
& new_size
) {
339 element_size_
= new_size
;
342 void GuestViewBase::GuestSizeChanged(const gfx::Size
& old_size
,
343 const gfx::Size
& new_size
) {
344 if (!auto_size_enabled_
)
346 guest_size_
= new_size
;
347 GuestSizeChangedDueToAutoSize(old_size
, new_size
);
350 void GuestViewBase::Destroy() {
351 if (is_being_destroyed_
)
354 is_being_destroyed_
= true;
356 DCHECK(web_contents());
358 // Give the derived class an opportunity to perform some cleanup.
361 // Invalidate weak pointers now so that bound callbacks cannot be called late
362 // into destruction. We must call this after WillDestroy because derived types
363 // may wish to access their openers.
364 weak_ptr_factory_
.InvalidateWeakPtrs();
366 // Give the content module an opportunity to perform some cleanup.
367 if (!destruction_callback_
.is_null())
368 destruction_callback_
.Run();
370 webcontents_guestview_map
.Get().erase(web_contents());
371 GuestViewManager::FromBrowserContext(browser_context_
)->
372 RemoveGuest(guest_instance_id_
);
373 pending_events_
.clear();
375 delete web_contents();
378 void GuestViewBase::SetAttachParams(const base::DictionaryValue
& params
) {
379 attach_params_
.reset(params
.DeepCopy());
380 attach_params_
->GetInteger(guestview::kParameterInstanceId
,
384 void GuestViewBase::SetOpener(GuestViewBase
* guest
) {
385 if (guest
&& guest
->IsViewType(GetViewType())) {
386 opener_
= guest
->weak_ptr_factory_
.GetWeakPtr();
388 opener_lifetime_observer_
.reset(new OpenerLifetimeObserver(this));
391 opener_
= base::WeakPtr
<GuestViewBase
>();
392 opener_lifetime_observer_
.reset();
395 void GuestViewBase::RegisterDestructionCallback(
396 const DestructionCallback
& callback
) {
397 destruction_callback_
= callback
;
400 void GuestViewBase::WillAttach(content::WebContents
* embedder_web_contents
,
401 int element_instance_id
) {
402 embedder_web_contents_
= embedder_web_contents
;
404 // If we are attaching to a different WebContents than the one that created
405 // the guest, we need to create a new LifetimeObserver.
406 if (embedder_web_contents
!= embedder_lifetime_observer_
->web_contents()) {
407 embedder_lifetime_observer_
.reset(
408 new EmbedderLifetimeObserver(this, embedder_web_contents
));
411 element_instance_id_
= element_instance_id
;
413 WillAttachToEmbedder();
416 void GuestViewBase::DidStopLoading(content::RenderViewHost
* render_view_host
) {
417 if (!IsDragAndDropEnabled()) {
418 const char script
[] = "window.addEventListener('dragstart', function() { "
419 " window.event.preventDefault(); "
421 render_view_host
->GetMainFrame()->ExecuteJavaScript(
422 base::ASCIIToUTF16(script
));
427 void GuestViewBase::RenderViewReady() {
431 void GuestViewBase::WebContentsDestroyed() {
432 // Let the derived class know that its WebContents is in the process of
433 // being destroyed. web_contents() is still valid at this point.
434 // TODO(fsamuel): This allows for reentrant code into WebContents during
435 // destruction. This could potentially lead to bugs. Perhaps we should get rid
443 void GuestViewBase::ActivateContents(WebContents
* web_contents
) {
444 if (!attached() || !embedder_web_contents()->GetDelegate())
447 embedder_web_contents()->GetDelegate()->ActivateContents(
448 embedder_web_contents());
451 void GuestViewBase::DeactivateContents(WebContents
* web_contents
) {
452 if (!attached() || !embedder_web_contents()->GetDelegate())
455 embedder_web_contents()->GetDelegate()->DeactivateContents(
456 embedder_web_contents());
459 void GuestViewBase::RunFileChooser(WebContents
* web_contents
,
460 const content::FileChooserParams
& params
) {
461 if (!attached() || !embedder_web_contents()->GetDelegate())
464 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents
, params
);
467 bool GuestViewBase::ShouldFocusPageAfterCrash() {
468 // Focus is managed elsewhere.
472 bool GuestViewBase::PreHandleGestureEvent(content::WebContents
* source
,
473 const blink::WebGestureEvent
& event
) {
474 return event
.type
== blink::WebGestureEvent::GesturePinchBegin
||
475 event
.type
== blink::WebGestureEvent::GesturePinchUpdate
||
476 event
.type
== blink::WebGestureEvent::GesturePinchEnd
;
479 GuestViewBase::~GuestViewBase() {
482 void GuestViewBase::DispatchEventToEmbedder(Event
* event
) {
483 scoped_ptr
<Event
> event_ptr(event
);
486 pending_events_
.push_back(linked_ptr
<Event
>(event_ptr
.release()));
490 EventFilteringInfo info
;
491 info
.SetInstanceID(view_instance_id_
);
492 scoped_ptr
<base::ListValue
> args(new base::ListValue());
493 args
->Append(event
->GetArguments().release());
495 EventRouter::DispatchEvent(
496 embedder_web_contents_
,
498 embedder_extension_id_
,
501 EventRouter::USER_GESTURE_UNKNOWN
,
505 void GuestViewBase::SendQueuedEvents() {
508 while (!pending_events_
.empty()) {
509 linked_ptr
<Event
> event_ptr
= pending_events_
.front();
510 pending_events_
.pop_front();
511 DispatchEventToEmbedder(event_ptr
.release());
515 void GuestViewBase::CompleteInit(const std::string
& embedder_extension_id
,
516 content::WebContents
* embedder_web_contents
,
517 const WebContentsCreatedCallback
& callback
,
518 content::WebContents
* guest_web_contents
) {
519 if (!guest_web_contents
) {
520 // The derived class did not create a WebContents so this class serves no
521 // purpose. Let's self-destruct.
527 embedder_extension_id
, embedder_web_contents
, guest_web_contents
);
528 callback
.Run(guest_web_contents
);
532 void GuestViewBase::RegisterGuestViewTypes() {
533 AppViewGuest::Register();
534 ExtensionOptionsGuest::Register();
535 MimeHandlerViewGuest::Register();
536 WebViewGuest::Register();
539 } // namespace extensions