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/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/app_view/app_view_guest.h"
18 #include "extensions/browser/guest_view/extension_options/extension_options_guest.h"
19 #include "extensions/browser/guest_view/guest_view_constants.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/features/feature.h"
25 #include "extensions/common/features/feature_provider.h"
26 #include "third_party/WebKit/public/web/WebInputEvent.h"
28 using content::WebContents
;
30 namespace extensions
{
34 typedef std::map
<std::string
, GuestViewBase::GuestCreationCallback
>
36 static base::LazyInstance
<GuestViewCreationMap
> guest_view_registry
=
37 LAZY_INSTANCE_INITIALIZER
;
39 typedef std::map
<WebContents
*, GuestViewBase
*> WebContentsGuestViewMap
;
40 static base::LazyInstance
<WebContentsGuestViewMap
> webcontents_guestview_map
=
41 LAZY_INSTANCE_INITIALIZER
;
45 GuestViewBase::Event::Event(const std::string
& name
,
46 scoped_ptr
<base::DictionaryValue
> args
)
47 : name_(name
), args_(args
.Pass()) {
50 GuestViewBase::Event::~Event() {
53 scoped_ptr
<base::DictionaryValue
> GuestViewBase::Event::GetArguments() {
57 // This observer ensures that the GuestViewBase destroys itself when its
58 // embedder goes away.
59 class GuestViewBase::EmbedderWebContentsObserver
: public WebContentsObserver
{
61 explicit EmbedderWebContentsObserver(GuestViewBase
* guest
)
62 : WebContentsObserver(guest
->embedder_web_contents()),
67 virtual ~EmbedderWebContentsObserver() {
70 // WebContentsObserver implementation.
71 virtual void WebContentsDestroyed() OVERRIDE
{
75 virtual void RenderViewHostChanged(
76 content::RenderViewHost
* old_host
,
77 content::RenderViewHost
* new_host
) OVERRIDE
{
81 virtual void RenderProcessGone(base::TerminationStatus status
) OVERRIDE
{
87 GuestViewBase
* guest_
;
93 guest_
->embedder_web_contents_
= NULL
;
94 guest_
->EmbedderDestroyed();
98 DISALLOW_COPY_AND_ASSIGN(EmbedderWebContentsObserver
);
101 GuestViewBase::GuestViewBase(content::BrowserContext
* browser_context
,
102 int guest_instance_id
)
103 : embedder_web_contents_(NULL
),
104 embedder_render_process_id_(0),
105 browser_context_(browser_context
),
106 guest_instance_id_(guest_instance_id
),
107 view_instance_id_(guestview::kInstanceIDNone
),
109 auto_size_enabled_(false),
110 weak_ptr_factory_(this) {
113 void GuestViewBase::Init(const std::string
& embedder_extension_id
,
114 content::WebContents
* embedder_web_contents
,
115 const base::DictionaryValue
& create_params
,
116 const WebContentsCreatedCallback
& callback
) {
121 Feature
* feature
= FeatureProvider::GetAPIFeatures()->GetFeature(
125 ProcessMap
* process_map
= ProcessMap::Get(browser_context());
128 const Extension
* embedder_extension
= ExtensionRegistry::Get(browser_context_
)
129 ->enabled_extensions()
130 .GetByID(embedder_extension_id
);
131 // Ok for |embedder_extension| to be NULL, the embedder might be WebUI.
133 CHECK(embedder_web_contents
);
134 int embedder_process_id
=
135 embedder_web_contents
->GetRenderProcessHost()->GetID();
137 Feature::Availability availability
= feature
->IsAvailableToContext(
139 process_map
->GetMostLikelyContextType(embedder_extension
,
140 embedder_process_id
),
141 embedder_web_contents
->GetLastCommittedURL());
142 if (!availability
.is_available()) {
147 CreateWebContents(embedder_extension_id
,
150 base::Bind(&GuestViewBase::CompleteInit
,
152 embedder_extension_id
,
157 void GuestViewBase::InitWithWebContents(
158 const std::string
& embedder_extension_id
,
159 int embedder_render_process_id
,
160 content::WebContents
* guest_web_contents
) {
161 DCHECK(guest_web_contents
);
162 content::RenderProcessHost
* embedder_render_process_host
=
163 content::RenderProcessHost::FromID(embedder_render_process_id
);
165 embedder_extension_id_
= embedder_extension_id
;
166 embedder_render_process_id_
= embedder_render_process_host
->GetID();
167 embedder_render_process_host
->AddObserver(this);
169 WebContentsObserver::Observe(guest_web_contents
);
170 guest_web_contents
->SetDelegate(this);
171 webcontents_guestview_map
.Get().insert(
172 std::make_pair(guest_web_contents
, this));
173 GuestViewManager::FromBrowserContext(browser_context_
)->
174 AddGuest(guest_instance_id_
, guest_web_contents
);
176 // Give the derived class an opportunity to perform additional initialization.
180 void GuestViewBase::SetAutoSize(bool enabled
,
181 const gfx::Size
& min_size
,
182 const gfx::Size
& max_size
) {
183 min_auto_size_
= min_size
;
184 min_auto_size_
.SetToMin(max_size
);
185 max_auto_size_
= max_size
;
186 max_auto_size_
.SetToMax(min_size
);
188 enabled
&= !min_auto_size_
.IsEmpty() && !max_auto_size_
.IsEmpty() &&
189 IsAutoSizeSupported();
190 if (!enabled
&& !auto_size_enabled_
)
193 auto_size_enabled_
= enabled
;
198 content::RenderViewHost
* rvh
= web_contents()->GetRenderViewHost();
199 if (auto_size_enabled_
) {
200 rvh
->EnableAutoResize(min_auto_size_
, max_auto_size_
);
202 rvh
->DisableAutoResize(element_size_
);
203 guest_size_
= element_size_
;
204 GuestSizeChangedDueToAutoSize(guest_size_
, element_size_
);
209 void GuestViewBase::RegisterGuestViewType(
210 const std::string
& view_type
,
211 const GuestCreationCallback
& callback
) {
212 GuestViewCreationMap::iterator it
=
213 guest_view_registry
.Get().find(view_type
);
214 DCHECK(it
== guest_view_registry
.Get().end());
215 guest_view_registry
.Get()[view_type
] = callback
;
219 GuestViewBase
* GuestViewBase::Create(
220 content::BrowserContext
* browser_context
,
221 int guest_instance_id
,
222 const std::string
& view_type
) {
223 if (guest_view_registry
.Get().empty())
224 RegisterGuestViewTypes();
226 GuestViewCreationMap::iterator it
=
227 guest_view_registry
.Get().find(view_type
);
228 if (it
== guest_view_registry
.Get().end()) {
232 return it
->second
.Run(browser_context
, guest_instance_id
);
236 GuestViewBase
* GuestViewBase::FromWebContents(WebContents
* web_contents
) {
237 WebContentsGuestViewMap
* guest_map
= webcontents_guestview_map
.Pointer();
238 WebContentsGuestViewMap::iterator it
= guest_map
->find(web_contents
);
239 return it
== guest_map
->end() ? NULL
: it
->second
;
243 GuestViewBase
* GuestViewBase::From(int embedder_process_id
,
244 int guest_instance_id
) {
245 content::RenderProcessHost
* host
=
246 content::RenderProcessHost::FromID(embedder_process_id
);
250 content::WebContents
* guest_web_contents
=
251 GuestViewManager::FromBrowserContext(host
->GetBrowserContext())->
252 GetGuestByInstanceIDSafely(guest_instance_id
, embedder_process_id
);
253 if (!guest_web_contents
)
256 return GuestViewBase::FromWebContents(guest_web_contents
);
260 bool GuestViewBase::IsGuest(WebContents
* web_contents
) {
261 return !!GuestViewBase::FromWebContents(web_contents
);
264 base::WeakPtr
<GuestViewBase
> GuestViewBase::AsWeakPtr() {
265 return weak_ptr_factory_
.GetWeakPtr();
268 bool GuestViewBase::IsAutoSizeSupported() const {
272 bool GuestViewBase::IsDragAndDropEnabled() const {
276 void GuestViewBase::RenderProcessExited(content::RenderProcessHost
* host
,
277 base::ProcessHandle handle
,
278 base::TerminationStatus status
,
280 // GuestViewBase tracks the lifetime of its embedder render process until it
281 // is attached to a particular embedder WebContents. At that point, its
282 // lifetime is restricted in scope to the lifetime of its embedder
285 CHECK_EQ(host
->GetID(), embedder_render_process_id());
287 // This code path may be reached if the embedder WebContents is killed for
288 // whatever reason immediately after a called to GuestViewInternal.createGuest
289 // and before attaching the new guest to a frame.
293 void GuestViewBase::Destroy() {
294 DCHECK(web_contents());
295 content::RenderProcessHost
* host
=
296 content::RenderProcessHost::FromID(embedder_render_process_id());
298 host
->RemoveObserver(this);
300 if (!destruction_callback_
.is_null())
301 destruction_callback_
.Run();
303 webcontents_guestview_map
.Get().erase(web_contents());
304 GuestViewManager::FromBrowserContext(browser_context_
)->
305 RemoveGuest(guest_instance_id_
);
306 pending_events_
.clear();
308 delete web_contents();
311 void GuestViewBase::DidAttach() {
312 // Give the derived class an opportunity to perform some actions.
313 DidAttachToEmbedder();
318 void GuestViewBase::ElementSizeChanged(const gfx::Size
& old_size
,
319 const gfx::Size
& new_size
) {
320 element_size_
= new_size
;
323 void GuestViewBase::GuestSizeChanged(const gfx::Size
& old_size
,
324 const gfx::Size
& new_size
) {
325 if (!auto_size_enabled_
)
327 guest_size_
= new_size
;
328 GuestSizeChangedDueToAutoSize(old_size
, new_size
);
331 void GuestViewBase::SetAttachParams(const base::DictionaryValue
& params
) {
332 attach_params_
.reset(params
.DeepCopy());
333 attach_params_
->GetInteger(guestview::kParameterInstanceId
,
337 void GuestViewBase::SetOpener(GuestViewBase
* guest
) {
338 if (guest
&& guest
->IsViewType(GetViewType())) {
339 opener_
= guest
->AsWeakPtr();
342 opener_
= base::WeakPtr
<GuestViewBase
>();
345 void GuestViewBase::RegisterDestructionCallback(
346 const DestructionCallback
& callback
) {
347 destruction_callback_
= callback
;
350 void GuestViewBase::WillAttach(content::WebContents
* embedder_web_contents
) {
351 // After attachment, this GuestViewBase's lifetime is restricted to the
352 // lifetime of its embedder WebContents. Observing the RenderProcessHost
353 // of the embedder is no longer necessary.
354 embedder_web_contents
->GetRenderProcessHost()->RemoveObserver(this);
355 embedder_web_contents_
= embedder_web_contents
;
356 embedder_web_contents_observer_
.reset(
357 new EmbedderWebContentsObserver(this));
359 WillAttachToEmbedder();
362 void GuestViewBase::DidStopLoading(content::RenderViewHost
* render_view_host
) {
363 if (!IsDragAndDropEnabled()) {
364 const char script
[] = "window.addEventListener('dragstart', function() { "
365 " window.event.preventDefault(); "
367 render_view_host
->GetMainFrame()->ExecuteJavaScript(
368 base::ASCIIToUTF16(script
));
373 void GuestViewBase::RenderViewReady() {
375 content::RenderViewHost
* rvh
= web_contents()->GetRenderViewHost();
376 if (auto_size_enabled_
) {
377 rvh
->EnableAutoResize(min_auto_size_
, max_auto_size_
);
379 rvh
->DisableAutoResize(element_size_
);
383 void GuestViewBase::WebContentsDestroyed() {
388 void GuestViewBase::ActivateContents(WebContents
* web_contents
) {
389 if (!attached() || !embedder_web_contents()->GetDelegate())
392 embedder_web_contents()->GetDelegate()->ActivateContents(
393 embedder_web_contents());
396 void GuestViewBase::DeactivateContents(WebContents
* web_contents
) {
397 if (!attached() || !embedder_web_contents()->GetDelegate())
400 embedder_web_contents()->GetDelegate()->DeactivateContents(
401 embedder_web_contents());
404 void GuestViewBase::RunFileChooser(WebContents
* web_contents
,
405 const content::FileChooserParams
& params
) {
406 if (!attached() || !embedder_web_contents()->GetDelegate())
409 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents
, params
);
412 bool GuestViewBase::ShouldFocusPageAfterCrash() {
413 // Focus is managed elsewhere.
417 bool GuestViewBase::PreHandleGestureEvent(content::WebContents
* source
,
418 const blink::WebGestureEvent
& event
) {
419 return event
.type
== blink::WebGestureEvent::GesturePinchBegin
||
420 event
.type
== blink::WebGestureEvent::GesturePinchUpdate
||
421 event
.type
== blink::WebGestureEvent::GesturePinchEnd
;
424 GuestViewBase::~GuestViewBase() {
427 void GuestViewBase::DispatchEventToEmbedder(Event
* event
) {
428 scoped_ptr
<Event
> event_ptr(event
);
431 pending_events_
.push_back(linked_ptr
<Event
>(event_ptr
.release()));
435 EventFilteringInfo info
;
436 info
.SetInstanceID(view_instance_id_
);
437 scoped_ptr
<base::ListValue
> args(new base::ListValue());
438 args
->Append(event
->GetArguments().release());
440 EventRouter::DispatchEvent(
441 embedder_web_contents_
,
443 embedder_extension_id_
,
446 EventRouter::USER_GESTURE_UNKNOWN
,
450 void GuestViewBase::SendQueuedEvents() {
453 while (!pending_events_
.empty()) {
454 linked_ptr
<Event
> event_ptr
= pending_events_
.front();
455 pending_events_
.pop_front();
456 DispatchEventToEmbedder(event_ptr
.release());
460 void GuestViewBase::CompleteInit(const std::string
& embedder_extension_id
,
461 int embedder_render_process_id
,
462 const WebContentsCreatedCallback
& callback
,
463 content::WebContents
* guest_web_contents
) {
464 if (!guest_web_contents
) {
465 // The derived class did not create a WebContents so this class serves no
466 // purpose. Let's self-destruct.
471 InitWithWebContents(embedder_extension_id
,
472 embedder_render_process_id
,
474 callback
.Run(guest_web_contents
);
478 void GuestViewBase::RegisterGuestViewTypes() {
479 AppViewGuest::Register();
480 ExtensionOptionsGuest::Register();
481 MimeHandlerViewGuest::Register();
482 WebViewGuest::Register();
485 } // namespace extensions