• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "extensions/browser/guest_view/guest_view_base.h"
6 
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"
28 
29 using content::WebContents;
30 
31 namespace extensions {
32 
33 namespace {
34 
35 typedef std::map<std::string, GuestViewBase::GuestCreationCallback>
36     GuestViewCreationMap;
37 static base::LazyInstance<GuestViewCreationMap> guest_view_registry =
38     LAZY_INSTANCE_INITIALIZER;
39 
40 typedef std::map<WebContents*, GuestViewBase*> WebContentsGuestViewMap;
41 static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map =
42     LAZY_INSTANCE_INITIALIZER;
43 
44 }  // namespace
45 
Event(const std::string & name,scoped_ptr<base::DictionaryValue> args)46 GuestViewBase::Event::Event(const std::string& name,
47                             scoped_ptr<base::DictionaryValue> args)
48     : name_(name), args_(args.Pass()) {
49 }
50 
~Event()51 GuestViewBase::Event::~Event() {
52 }
53 
GetArguments()54 scoped_ptr<base::DictionaryValue> GuestViewBase::Event::GetArguments() {
55   return args_.Pass();
56 }
57 
58 // This observer ensures that the GuestViewBase destroys itself when its
59 // embedder goes away.
60 class GuestViewBase::EmbedderWebContentsObserver : public WebContentsObserver {
61  public:
EmbedderWebContentsObserver(GuestViewBase * guest)62   explicit EmbedderWebContentsObserver(GuestViewBase* guest)
63       : WebContentsObserver(guest->embedder_web_contents()),
64         destroyed_(false),
65         guest_(guest) {
66   }
67 
~EmbedderWebContentsObserver()68   virtual ~EmbedderWebContentsObserver() {
69   }
70 
71   // WebContentsObserver implementation.
WebContentsDestroyed()72   virtual void WebContentsDestroyed() OVERRIDE {
73     Destroy();
74   }
75 
RenderViewHostChanged(content::RenderViewHost * old_host,content::RenderViewHost * new_host)76   virtual void RenderViewHostChanged(
77       content::RenderViewHost* old_host,
78       content::RenderViewHost* new_host) OVERRIDE {
79     Destroy();
80   }
81 
RenderProcessGone(base::TerminationStatus status)82   virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
83     Destroy();
84   }
85 
86  private:
87   bool destroyed_;
88   GuestViewBase* guest_;
89 
Destroy()90   void Destroy() {
91     if (destroyed_)
92       return;
93     destroyed_ = true;
94     guest_->embedder_web_contents_ = NULL;
95     guest_->EmbedderDestroyed();
96     guest_->Destroy();
97   }
98 
99   DISALLOW_COPY_AND_ASSIGN(EmbedderWebContentsObserver);
100 };
101 
GuestViewBase(content::BrowserContext * browser_context,int guest_instance_id)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),
110       initialized_(false),
111       auto_size_enabled_(false),
112       weak_ptr_factory_(this) {
113 }
114 
Init(const std::string & embedder_extension_id,content::WebContents * embedder_web_contents,const base::DictionaryValue & create_params,const WebContentsCreatedCallback & callback)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) {
119   if (initialized_)
120     return;
121   initialized_ = true;
122 
123   Feature* feature = FeatureProvider::GetAPIFeatures()->GetFeature(
124       GetAPINamespace());
125   CHECK(feature);
126 
127   ProcessMap* process_map = ProcessMap::Get(browser_context());
128   CHECK(process_map);
129 
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.
134 
135   CHECK(embedder_web_contents);
136   int embedder_process_id =
137       embedder_web_contents->GetRenderProcessHost()->GetID();
138 
139   const GURL& embedder_site_url = embedder_web_contents->GetLastCommittedURL();
140   Feature::Availability availability = feature->IsAvailableToContext(
141       embedder_extension,
142       process_map->GetMostLikelyContextType(embedder_extension,
143                                             embedder_process_id),
144       embedder_site_url);
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.
148     delete this;
149     callback.Run(NULL);
150     return;
151   }
152 
153   CreateWebContents(embedder_extension_id,
154                     embedder_process_id,
155                     embedder_site_url,
156                     create_params,
157                     base::Bind(&GuestViewBase::CompleteInit,
158                                AsWeakPtr(),
159                                embedder_extension_id,
160                                embedder_process_id,
161                                callback));
162 }
163 
InitWithWebContents(const std::string & embedder_extension_id,int embedder_render_process_id,content::WebContents * guest_web_contents)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);
171 
172   embedder_extension_id_ = embedder_extension_id;
173   embedder_render_process_id_ = embedder_render_process_host->GetID();
174   embedder_render_process_host->AddObserver(this);
175 
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);
182 
183   // Give the derived class an opportunity to perform additional initialization.
184   DidInitialize();
185 }
186 
SetAutoSize(bool enabled,const gfx::Size & min_size,const gfx::Size & max_size)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);
194 
195   enabled &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty() &&
196       IsAutoSizeSupported();
197   if (!enabled && !auto_size_enabled_)
198     return;
199 
200   auto_size_enabled_ = enabled;
201 
202   if (!attached())
203     return;
204 
205   content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
206   if (auto_size_enabled_) {
207     rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
208   } else {
209     rvh->DisableAutoResize(element_size_);
210     guest_size_ = element_size_;
211     GuestSizeChangedDueToAutoSize(guest_size_, element_size_);
212   }
213 }
214 
215 // static
RegisterGuestViewType(const std::string & view_type,const GuestCreationCallback & callback)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;
223 }
224 
225 // static
Create(content::BrowserContext * browser_context,int guest_instance_id,const std::string & view_type)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();
232 
233   GuestViewCreationMap::iterator it =
234       guest_view_registry.Get().find(view_type);
235   if (it == guest_view_registry.Get().end()) {
236     NOTREACHED();
237     return NULL;
238   }
239   return it->second.Run(browser_context, guest_instance_id);
240 }
241 
242 // static
FromWebContents(WebContents * web_contents)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;
247 }
248 
249 // static
From(int embedder_process_id,int guest_instance_id)250 GuestViewBase* GuestViewBase::From(int embedder_process_id,
251                                    int guest_instance_id) {
252   content::RenderProcessHost* host =
253       content::RenderProcessHost::FromID(embedder_process_id);
254   if (!host)
255     return NULL;
256 
257   content::WebContents* guest_web_contents =
258       GuestViewManager::FromBrowserContext(host->GetBrowserContext())->
259           GetGuestByInstanceIDSafely(guest_instance_id, embedder_process_id);
260   if (!guest_web_contents)
261     return NULL;
262 
263   return GuestViewBase::FromWebContents(guest_web_contents);
264 }
265 
266 // static
IsGuest(WebContents * web_contents)267 bool GuestViewBase::IsGuest(WebContents* web_contents) {
268   return !!GuestViewBase::FromWebContents(web_contents);
269 }
270 
AsWeakPtr()271 base::WeakPtr<GuestViewBase> GuestViewBase::AsWeakPtr() {
272   return weak_ptr_factory_.GetWeakPtr();
273 }
274 
IsAutoSizeSupported() const275 bool GuestViewBase::IsAutoSizeSupported() const {
276   return false;
277 }
278 
IsDragAndDropEnabled() const279 bool GuestViewBase::IsDragAndDropEnabled() const {
280   return false;
281 }
282 
RenderProcessExited(content::RenderProcessHost * host,base::ProcessHandle handle,base::TerminationStatus status,int exit_code)283 void GuestViewBase::RenderProcessExited(content::RenderProcessHost* host,
284                                         base::ProcessHandle handle,
285                                         base::TerminationStatus status,
286                                         int exit_code) {
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
290   // WebContents.
291   CHECK(!attached());
292   CHECK_EQ(host->GetID(), embedder_render_process_id());
293 
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.
297   Destroy();
298 }
299 
Destroy()300 void GuestViewBase::Destroy() {
301   DCHECK(web_contents());
302   content::RenderProcessHost* host =
303       content::RenderProcessHost::FromID(embedder_render_process_id());
304   if (host)
305     host->RemoveObserver(this);
306   WillDestroy();
307   if (!destruction_callback_.is_null())
308     destruction_callback_.Run();
309 
310   webcontents_guestview_map.Get().erase(web_contents());
311   GuestViewManager::FromBrowserContext(browser_context_)->
312       RemoveGuest(guest_instance_id_);
313   pending_events_.clear();
314 
315   delete web_contents();
316 }
317 
DidAttach(int guest_proxy_routing_id)318 void GuestViewBase::DidAttach(int guest_proxy_routing_id) {
319   // Give the derived class an opportunity to perform some actions.
320   DidAttachToEmbedder();
321 
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));
327 
328   SendQueuedEvents();
329 }
330 
ElementSizeChanged(const gfx::Size & old_size,const gfx::Size & new_size)331 void GuestViewBase::ElementSizeChanged(const gfx::Size& old_size,
332                                        const gfx::Size& new_size) {
333   element_size_ = new_size;
334 }
335 
GuestSizeChanged(const gfx::Size & old_size,const gfx::Size & new_size)336 void GuestViewBase::GuestSizeChanged(const gfx::Size& old_size,
337                                      const gfx::Size& new_size) {
338   if (!auto_size_enabled_)
339     return;
340   guest_size_ = new_size;
341   GuestSizeChangedDueToAutoSize(old_size, new_size);
342 }
343 
SetAttachParams(const base::DictionaryValue & params)344 void GuestViewBase::SetAttachParams(const base::DictionaryValue& params) {
345   attach_params_.reset(params.DeepCopy());
346   attach_params_->GetInteger(guestview::kParameterInstanceId,
347                              &view_instance_id_);
348 }
349 
SetOpener(GuestViewBase * guest)350 void GuestViewBase::SetOpener(GuestViewBase* guest) {
351   if (guest && guest->IsViewType(GetViewType())) {
352     opener_ = guest->AsWeakPtr();
353     return;
354   }
355   opener_ = base::WeakPtr<GuestViewBase>();
356 }
357 
RegisterDestructionCallback(const DestructionCallback & callback)358 void GuestViewBase::RegisterDestructionCallback(
359     const DestructionCallback& callback) {
360   destruction_callback_ = callback;
361 }
362 
WillAttach(content::WebContents * embedder_web_contents,int element_instance_id)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;
373 
374   WillAttachToEmbedder();
375 }
376 
DidStopLoading(content::RenderViewHost * render_view_host)377 void GuestViewBase::DidStopLoading(content::RenderViewHost* render_view_host) {
378   if (!IsDragAndDropEnabled()) {
379     const char script[] = "window.addEventListener('dragstart', function() { "
380                           "  window.event.preventDefault(); "
381                           "});";
382     render_view_host->GetMainFrame()->ExecuteJavaScript(
383         base::ASCIIToUTF16(script));
384   }
385   DidStopLoading();
386 }
387 
RenderViewReady()388 void GuestViewBase::RenderViewReady() {
389   GuestReady();
390 }
391 
WebContentsDestroyed()392 void GuestViewBase::WebContentsDestroyed() {
393   GuestDestroyed();
394   delete this;
395 }
396 
ActivateContents(WebContents * web_contents)397 void GuestViewBase::ActivateContents(WebContents* web_contents) {
398   if (!attached() || !embedder_web_contents()->GetDelegate())
399     return;
400 
401   embedder_web_contents()->GetDelegate()->ActivateContents(
402       embedder_web_contents());
403 }
404 
DeactivateContents(WebContents * web_contents)405 void GuestViewBase::DeactivateContents(WebContents* web_contents) {
406   if (!attached() || !embedder_web_contents()->GetDelegate())
407     return;
408 
409   embedder_web_contents()->GetDelegate()->DeactivateContents(
410       embedder_web_contents());
411 }
412 
RunFileChooser(WebContents * web_contents,const content::FileChooserParams & params)413 void GuestViewBase::RunFileChooser(WebContents* web_contents,
414                                    const content::FileChooserParams& params) {
415   if (!attached() || !embedder_web_contents()->GetDelegate())
416     return;
417 
418   embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params);
419 }
420 
ShouldFocusPageAfterCrash()421 bool GuestViewBase::ShouldFocusPageAfterCrash() {
422   // Focus is managed elsewhere.
423   return false;
424 }
425 
PreHandleGestureEvent(content::WebContents * source,const blink::WebGestureEvent & event)426 bool GuestViewBase::PreHandleGestureEvent(content::WebContents* source,
427                                          const blink::WebGestureEvent& event) {
428   return event.type == blink::WebGestureEvent::GesturePinchBegin ||
429       event.type == blink::WebGestureEvent::GesturePinchUpdate ||
430       event.type == blink::WebGestureEvent::GesturePinchEnd;
431 }
432 
~GuestViewBase()433 GuestViewBase::~GuestViewBase() {
434 }
435 
DispatchEventToEmbedder(Event * event)436 void GuestViewBase::DispatchEventToEmbedder(Event* event) {
437   scoped_ptr<Event> event_ptr(event);
438 
439   if (!attached()) {
440     pending_events_.push_back(linked_ptr<Event>(event_ptr.release()));
441     return;
442   }
443 
444   EventFilteringInfo info;
445   info.SetInstanceID(view_instance_id_);
446   scoped_ptr<base::ListValue> args(new base::ListValue());
447   args->Append(event->GetArguments().release());
448 
449   EventRouter::DispatchEvent(
450       embedder_web_contents_,
451       browser_context_,
452       embedder_extension_id_,
453       event->name(),
454       args.Pass(),
455       EventRouter::USER_GESTURE_UNKNOWN,
456       info);
457 }
458 
SendQueuedEvents()459 void GuestViewBase::SendQueuedEvents() {
460   if (!attached())
461     return;
462   while (!pending_events_.empty()) {
463     linked_ptr<Event> event_ptr = pending_events_.front();
464     pending_events_.pop_front();
465     DispatchEventToEmbedder(event_ptr.release());
466   }
467 }
468 
CompleteInit(const std::string & embedder_extension_id,int embedder_render_process_id,const WebContentsCreatedCallback & callback,content::WebContents * guest_web_contents)469 void GuestViewBase::CompleteInit(const std::string& embedder_extension_id,
470                                  int embedder_render_process_id,
471                                  const WebContentsCreatedCallback& callback,
472                                  content::WebContents* guest_web_contents) {
473   if (!guest_web_contents) {
474     // The derived class did not create a WebContents so this class serves no
475     // purpose. Let's self-destruct.
476     delete this;
477     callback.Run(NULL);
478     return;
479   }
480   InitWithWebContents(embedder_extension_id,
481                       embedder_render_process_id,
482                       guest_web_contents);
483   callback.Run(guest_web_contents);
484 }
485 
486 // static
RegisterGuestViewTypes()487 void GuestViewBase::RegisterGuestViewTypes() {
488   AppViewGuest::Register();
489   ExtensionOptionsGuest::Register();
490   MimeHandlerViewGuest::Register();
491   WebViewGuest::Register();
492 }
493 
494 }  // namespace extensions
495