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_manager.h"
20 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
21 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
22 #include "extensions/browser/process_map.h"
23 #include "extensions/common/extension_messages.h"
24 #include "extensions/common/features/feature.h"
25 #include "extensions/common/features/feature_provider.h"
26 #include "extensions/common/guest_view/guest_view_constants.h"
27 #include "third_party/WebKit/public/web/WebInputEvent.h"
29 using content::WebContents;
31 namespace extensions {
35 typedef std::map<std::string, GuestViewBase::GuestCreationCallback>
37 static base::LazyInstance<GuestViewCreationMap> guest_view_registry =
38 LAZY_INSTANCE_INITIALIZER;
40 typedef std::map<WebContents*, GuestViewBase*> WebContentsGuestViewMap;
41 static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map =
42 LAZY_INSTANCE_INITIALIZER;
46 GuestViewBase::Event::Event(const std::string& name,
47 scoped_ptr<base::DictionaryValue> args)
48 : name_(name), args_(args.Pass()) {
51 GuestViewBase::Event::~Event() {
54 scoped_ptr<base::DictionaryValue> GuestViewBase::Event::GetArguments() {
58 // This observer ensures that the GuestViewBase destroys itself when its
59 // embedder goes away.
60 class GuestViewBase::EmbedderWebContentsObserver : public WebContentsObserver {
62 explicit EmbedderWebContentsObserver(GuestViewBase* guest)
63 : WebContentsObserver(guest->embedder_web_contents()),
68 virtual ~EmbedderWebContentsObserver() {
71 // WebContentsObserver implementation.
72 virtual void WebContentsDestroyed() OVERRIDE {
76 virtual void RenderViewHostChanged(
77 content::RenderViewHost* old_host,
78 content::RenderViewHost* new_host) OVERRIDE {
82 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
88 GuestViewBase* guest_;
94 guest_->embedder_web_contents_ = NULL;
95 guest_->EmbedderDestroyed();
99 DISALLOW_COPY_AND_ASSIGN(EmbedderWebContentsObserver);
102 GuestViewBase::GuestViewBase(content::BrowserContext* browser_context,
103 int guest_instance_id)
104 : embedder_web_contents_(NULL),
105 embedder_render_process_id_(0),
106 browser_context_(browser_context),
107 guest_instance_id_(guest_instance_id),
108 view_instance_id_(guestview::kInstanceIDNone),
109 element_instance_id_(guestview::kInstanceIDNone),
111 auto_size_enabled_(false),
112 weak_ptr_factory_(this) {
115 void GuestViewBase::Init(const std::string& embedder_extension_id,
116 content::WebContents* embedder_web_contents,
117 const base::DictionaryValue& create_params,
118 const WebContentsCreatedCallback& callback) {
123 Feature* feature = FeatureProvider::GetAPIFeatures()->GetFeature(
127 ProcessMap* process_map = ProcessMap::Get(browser_context());
130 const Extension* embedder_extension = ExtensionRegistry::Get(browser_context_)
131 ->enabled_extensions()
132 .GetByID(embedder_extension_id);
133 // Ok for |embedder_extension| to be NULL, the embedder might be WebUI.
135 CHECK(embedder_web_contents);
136 int embedder_process_id =
137 embedder_web_contents->GetRenderProcessHost()->GetID();
139 const GURL& embedder_site_url = embedder_web_contents->GetLastCommittedURL();
140 Feature::Availability availability = feature->IsAvailableToContext(
142 process_map->GetMostLikelyContextType(embedder_extension,
143 embedder_process_id),
145 if (!availability.is_available()) {
146 // The derived class did not create a WebContents so this class serves no
147 // purpose. Let's self-destruct.
153 CreateWebContents(embedder_extension_id,
157 base::Bind(&GuestViewBase::CompleteInit,
159 embedder_extension_id,
164 void GuestViewBase::InitWithWebContents(
165 const std::string& embedder_extension_id,
166 int embedder_render_process_id,
167 content::WebContents* guest_web_contents) {
168 DCHECK(guest_web_contents);
169 content::RenderProcessHost* embedder_render_process_host =
170 content::RenderProcessHost::FromID(embedder_render_process_id);
172 embedder_extension_id_ = embedder_extension_id;
173 embedder_render_process_id_ = embedder_render_process_host->GetID();
174 embedder_render_process_host->AddObserver(this);
176 WebContentsObserver::Observe(guest_web_contents);
177 guest_web_contents->SetDelegate(this);
178 webcontents_guestview_map.Get().insert(
179 std::make_pair(guest_web_contents, this));
180 GuestViewManager::FromBrowserContext(browser_context_)->
181 AddGuest(guest_instance_id_, guest_web_contents);
183 // Give the derived class an opportunity to perform additional initialization.
187 void GuestViewBase::SetAutoSize(bool enabled,
188 const gfx::Size& min_size,
189 const gfx::Size& max_size) {
190 min_auto_size_ = min_size;
191 min_auto_size_.SetToMin(max_size);
192 max_auto_size_ = max_size;
193 max_auto_size_.SetToMax(min_size);
195 enabled &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty() &&
196 IsAutoSizeSupported();
197 if (!enabled && !auto_size_enabled_)
200 auto_size_enabled_ = enabled;
205 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
206 if (auto_size_enabled_) {
207 rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
209 rvh->DisableAutoResize(element_size_);
210 guest_size_ = element_size_;
211 GuestSizeChangedDueToAutoSize(guest_size_, element_size_);
216 void GuestViewBase::RegisterGuestViewType(
217 const std::string& view_type,
218 const GuestCreationCallback& callback) {
219 GuestViewCreationMap::iterator it =
220 guest_view_registry.Get().find(view_type);
221 DCHECK(it == guest_view_registry.Get().end());
222 guest_view_registry.Get()[view_type] = callback;
226 GuestViewBase* GuestViewBase::Create(
227 content::BrowserContext* browser_context,
228 int guest_instance_id,
229 const std::string& view_type) {
230 if (guest_view_registry.Get().empty())
231 RegisterGuestViewTypes();
233 GuestViewCreationMap::iterator it =
234 guest_view_registry.Get().find(view_type);
235 if (it == guest_view_registry.Get().end()) {
239 return it->second.Run(browser_context, guest_instance_id);
243 GuestViewBase* GuestViewBase::FromWebContents(WebContents* web_contents) {
244 WebContentsGuestViewMap* guest_map = webcontents_guestview_map.Pointer();
245 WebContentsGuestViewMap::iterator it = guest_map->find(web_contents);
246 return it == guest_map->end() ? NULL : it->second;
250 GuestViewBase* GuestViewBase::From(int embedder_process_id,
251 int guest_instance_id) {
252 content::RenderProcessHost* host =
253 content::RenderProcessHost::FromID(embedder_process_id);
257 content::WebContents* guest_web_contents =
258 GuestViewManager::FromBrowserContext(host->GetBrowserContext())->
259 GetGuestByInstanceIDSafely(guest_instance_id, embedder_process_id);
260 if (!guest_web_contents)
263 return GuestViewBase::FromWebContents(guest_web_contents);
267 bool GuestViewBase::IsGuest(WebContents* web_contents) {
268 return !!GuestViewBase::FromWebContents(web_contents);
271 base::WeakPtr<GuestViewBase> GuestViewBase::AsWeakPtr() {
272 return weak_ptr_factory_.GetWeakPtr();
275 bool GuestViewBase::IsAutoSizeSupported() const {
279 bool GuestViewBase::IsDragAndDropEnabled() const {
283 void GuestViewBase::RenderProcessExited(content::RenderProcessHost* host,
284 base::ProcessHandle handle,
285 base::TerminationStatus status,
287 // GuestViewBase tracks the lifetime of its embedder render process until it
288 // is attached to a particular embedder WebContents. At that point, its
289 // lifetime is restricted in scope to the lifetime of its embedder
292 CHECK_EQ(host->GetID(), embedder_render_process_id());
294 // This code path may be reached if the embedder WebContents is killed for
295 // whatever reason immediately after a called to GuestViewInternal.createGuest
296 // and before attaching the new guest to a frame.
300 void GuestViewBase::Destroy() {
301 DCHECK(web_contents());
302 content::RenderProcessHost* host =
303 content::RenderProcessHost::FromID(embedder_render_process_id());
305 host->RemoveObserver(this);
307 if (!destruction_callback_.is_null())
308 destruction_callback_.Run();
310 webcontents_guestview_map.Get().erase(web_contents());
311 GuestViewManager::FromBrowserContext(browser_context_)->
312 RemoveGuest(guest_instance_id_);
313 pending_events_.clear();
315 delete web_contents();
318 void GuestViewBase::DidAttach(int guest_proxy_routing_id) {
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 embedder_web_contents()->GetMainFrame()->GetRoutingID(),
325 element_instance_id_,
326 guest_proxy_routing_id));
331 void GuestViewBase::ElementSizeChanged(const gfx::Size& old_size,
332 const gfx::Size& new_size) {
333 element_size_ = new_size;
336 void GuestViewBase::GuestSizeChanged(const gfx::Size& old_size,
337 const gfx::Size& new_size) {
338 if (!auto_size_enabled_)
340 guest_size_ = new_size;
341 GuestSizeChangedDueToAutoSize(old_size, new_size);
344 void GuestViewBase::SetAttachParams(const base::DictionaryValue& params) {
345 attach_params_.reset(params.DeepCopy());
346 attach_params_->GetInteger(guestview::kParameterInstanceId,
350 void GuestViewBase::SetOpener(GuestViewBase* guest) {
351 if (guest && guest->IsViewType(GetViewType())) {
352 opener_ = guest->AsWeakPtr();
355 opener_ = base::WeakPtr<GuestViewBase>();
358 void GuestViewBase::RegisterDestructionCallback(
359 const DestructionCallback& callback) {
360 destruction_callback_ = callback;
363 void GuestViewBase::WillAttach(content::WebContents* embedder_web_contents,
364 int element_instance_id) {
365 // After attachment, this GuestViewBase's lifetime is restricted to the
366 // lifetime of its embedder WebContents. Observing the RenderProcessHost
367 // of the embedder is no longer necessary.
368 embedder_web_contents->GetRenderProcessHost()->RemoveObserver(this);
369 embedder_web_contents_ = embedder_web_contents;
370 embedder_web_contents_observer_.reset(
371 new EmbedderWebContentsObserver(this));
372 element_instance_id_ = element_instance_id;
374 WillAttachToEmbedder();
377 void GuestViewBase::DidStopLoading(content::RenderViewHost* render_view_host) {
378 if (!IsDragAndDropEnabled()) {
379 const char script[] = "window.addEventListener('dragstart', function() { "
380 " window.event.preventDefault(); "
382 render_view_host->GetMainFrame()->ExecuteJavaScript(
383 base::ASCIIToUTF16(script));
388 void GuestViewBase::RenderViewReady() {
390 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
391 if (auto_size_enabled_) {
392 rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
394 rvh->DisableAutoResize(element_size_);
398 void GuestViewBase::WebContentsDestroyed() {
403 void GuestViewBase::ActivateContents(WebContents* web_contents) {
404 if (!attached() || !embedder_web_contents()->GetDelegate())
407 embedder_web_contents()->GetDelegate()->ActivateContents(
408 embedder_web_contents());
411 void GuestViewBase::DeactivateContents(WebContents* web_contents) {
412 if (!attached() || !embedder_web_contents()->GetDelegate())
415 embedder_web_contents()->GetDelegate()->DeactivateContents(
416 embedder_web_contents());
419 void GuestViewBase::RunFileChooser(WebContents* web_contents,
420 const content::FileChooserParams& params) {
421 if (!attached() || !embedder_web_contents()->GetDelegate())
424 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params);
427 bool GuestViewBase::ShouldFocusPageAfterCrash() {
428 // Focus is managed elsewhere.
432 bool GuestViewBase::PreHandleGestureEvent(content::WebContents* source,
433 const blink::WebGestureEvent& event) {
434 return event.type == blink::WebGestureEvent::GesturePinchBegin ||
435 event.type == blink::WebGestureEvent::GesturePinchUpdate ||
436 event.type == blink::WebGestureEvent::GesturePinchEnd;
439 GuestViewBase::~GuestViewBase() {
442 void GuestViewBase::DispatchEventToEmbedder(Event* event) {
443 scoped_ptr<Event> event_ptr(event);
446 pending_events_.push_back(linked_ptr<Event>(event_ptr.release()));
450 EventFilteringInfo info;
451 info.SetInstanceID(view_instance_id_);
452 scoped_ptr<base::ListValue> args(new base::ListValue());
453 args->Append(event->GetArguments().release());
455 EventRouter::DispatchEvent(
456 embedder_web_contents_,
458 embedder_extension_id_,
461 EventRouter::USER_GESTURE_UNKNOWN,
465 void GuestViewBase::SendQueuedEvents() {
468 while (!pending_events_.empty()) {
469 linked_ptr<Event> event_ptr = pending_events_.front();
470 pending_events_.pop_front();
471 DispatchEventToEmbedder(event_ptr.release());
475 void GuestViewBase::CompleteInit(const std::string& embedder_extension_id,
476 int embedder_render_process_id,
477 const WebContentsCreatedCallback& callback,
478 content::WebContents* guest_web_contents) {
479 if (!guest_web_contents) {
480 // The derived class did not create a WebContents so this class serves no
481 // purpose. Let's self-destruct.
486 InitWithWebContents(embedder_extension_id,
487 embedder_render_process_id,
489 callback.Run(guest_web_contents);
493 void GuestViewBase::RegisterGuestViewTypes() {
494 AppViewGuest::Register();
495 ExtensionOptionsGuest::Register();
496 MimeHandlerViewGuest::Register();
497 WebViewGuest::Register();
500 } // namespace extensions