Home | History | Annotate | Download | only in guest_view
      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 
     46 GuestViewBase::Event::Event(const std::string& name,
     47                             scoped_ptr<base::DictionaryValue> args)
     48     : name_(name), args_(args.Pass()) {
     49 }
     50 
     51 GuestViewBase::Event::~Event() {
     52 }
     53 
     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:
     62   explicit EmbedderWebContentsObserver(GuestViewBase* guest)
     63       : WebContentsObserver(guest->embedder_web_contents()),
     64         destroyed_(false),
     65         guest_(guest) {
     66   }
     67 
     68   virtual ~EmbedderWebContentsObserver() {
     69   }
     70 
     71   // WebContentsObserver implementation.
     72   virtual void WebContentsDestroyed() OVERRIDE {
     73     Destroy();
     74   }
     75 
     76   virtual void RenderViewHostChanged(
     77       content::RenderViewHost* old_host,
     78       content::RenderViewHost* new_host) OVERRIDE {
     79     Destroy();
     80   }
     81 
     82   virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
     83     Destroy();
     84   }
     85 
     86  private:
     87   bool destroyed_;
     88   GuestViewBase* guest_;
     89 
     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 
    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 
    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 
    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 
    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
    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
    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
    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
    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
    267 bool GuestViewBase::IsGuest(WebContents* web_contents) {
    268   return !!GuestViewBase::FromWebContents(web_contents);
    269 }
    270 
    271 base::WeakPtr<GuestViewBase> GuestViewBase::AsWeakPtr() {
    272   return weak_ptr_factory_.GetWeakPtr();
    273 }
    274 
    275 bool GuestViewBase::IsAutoSizeSupported() const {
    276   return false;
    277 }
    278 
    279 bool GuestViewBase::IsDragAndDropEnabled() const {
    280   return false;
    281 }
    282 
    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 
    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 
    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 
    331 void GuestViewBase::ElementSizeChanged(const gfx::Size& old_size,
    332                                        const gfx::Size& new_size) {
    333   element_size_ = new_size;
    334 }
    335 
    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 
    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 
    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 
    358 void GuestViewBase::RegisterDestructionCallback(
    359     const DestructionCallback& callback) {
    360   destruction_callback_ = callback;
    361 }
    362 
    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 
    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 
    388 void GuestViewBase::RenderViewReady() {
    389   GuestReady();
    390 }
    391 
    392 void GuestViewBase::WebContentsDestroyed() {
    393   GuestDestroyed();
    394   delete this;
    395 }
    396 
    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 
    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 
    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 
    421 bool GuestViewBase::ShouldFocusPageAfterCrash() {
    422   // Focus is managed elsewhere.
    423   return false;
    424 }
    425 
    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 
    433 GuestViewBase::~GuestViewBase() {
    434 }
    435 
    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 
    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 
    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
    487 void GuestViewBase::RegisterGuestViewTypes() {
    488   AppViewGuest::Register();
    489   ExtensionOptionsGuest::Register();
    490   MimeHandlerViewGuest::Register();
    491   WebViewGuest::Register();
    492 }
    493 
    494 }  // namespace extensions
    495