guest_view_base.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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/guest_view_constants.h"
18#include "extensions/browser/guest_view/guest_view_manager.h"
19#include "extensions/browser/process_map.h"
20#include "extensions/common/features/feature.h"
21#include "extensions/common/features/feature_provider.h"
22#include "third_party/WebKit/public/web/WebInputEvent.h"
23
24using content::WebContents;
25
26namespace extensions {
27
28namespace {
29
30typedef std::map<std::string, GuestViewBase::GuestCreationCallback>
31    GuestViewCreationMap;
32static base::LazyInstance<GuestViewCreationMap> guest_view_registry =
33    LAZY_INSTANCE_INITIALIZER;
34
35typedef std::map<WebContents*, GuestViewBase*> WebContentsGuestViewMap;
36static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map =
37    LAZY_INSTANCE_INITIALIZER;
38
39}  // namespace
40
41GuestViewBase::Event::Event(const std::string& name,
42                            scoped_ptr<base::DictionaryValue> args)
43    : name_(name), args_(args.Pass()) {
44}
45
46GuestViewBase::Event::~Event() {
47}
48
49scoped_ptr<base::DictionaryValue> GuestViewBase::Event::GetArguments() {
50  return args_.Pass();
51}
52
53// This observer ensures that the GuestViewBase destroys itself when its
54// embedder goes away.
55class GuestViewBase::EmbedderWebContentsObserver : public WebContentsObserver {
56 public:
57  explicit EmbedderWebContentsObserver(GuestViewBase* guest)
58      : WebContentsObserver(guest->embedder_web_contents()),
59        destroyed_(false),
60        guest_(guest) {
61  }
62
63  virtual ~EmbedderWebContentsObserver() {
64  }
65
66  // WebContentsObserver implementation.
67  virtual void WebContentsDestroyed() OVERRIDE {
68    Destroy();
69  }
70
71  virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
72    Destroy();
73  }
74
75 private:
76  bool destroyed_;
77  GuestViewBase* guest_;
78
79  void Destroy() {
80    if (destroyed_)
81      return;
82    destroyed_ = true;
83    guest_->embedder_web_contents_ = NULL;
84    guest_->EmbedderDestroyed();
85    guest_->Destroy();
86  }
87
88  DISALLOW_COPY_AND_ASSIGN(EmbedderWebContentsObserver);
89};
90
91GuestViewBase::GuestViewBase(content::BrowserContext* browser_context,
92                             int guest_instance_id)
93    : embedder_web_contents_(NULL),
94      embedder_render_process_id_(0),
95      browser_context_(browser_context),
96      guest_instance_id_(guest_instance_id),
97      view_instance_id_(guestview::kInstanceIDNone),
98      initialized_(false),
99      auto_size_enabled_(false),
100      weak_ptr_factory_(this) {
101}
102
103void GuestViewBase::Init(const std::string& embedder_extension_id,
104                         content::WebContents* embedder_web_contents,
105                         const base::DictionaryValue& create_params,
106                         const WebContentsCreatedCallback& callback) {
107  if (initialized_)
108    return;
109  initialized_ = true;
110
111  Feature* feature = FeatureProvider::GetAPIFeatures()->GetFeature(
112      GetAPINamespace());
113  CHECK(feature);
114
115  ProcessMap* process_map = ProcessMap::Get(browser_context());
116  CHECK(process_map);
117
118  const Extension* embedder_extension = ExtensionRegistry::Get(browser_context_)
119          ->enabled_extensions()
120          .GetByID(embedder_extension_id);
121  // Ok for |embedder_extension| to be NULL, the embedder might be WebUI.
122
123  CHECK(embedder_web_contents);
124  int embedder_process_id =
125      embedder_web_contents->GetRenderProcessHost()->GetID();
126
127  Feature::Availability availability = feature->IsAvailableToContext(
128      embedder_extension,
129      process_map->GetMostLikelyContextType(embedder_extension,
130                                            embedder_process_id),
131      embedder_web_contents->GetLastCommittedURL());
132  if (!availability.is_available()) {
133    callback.Run(NULL);
134    return;
135  }
136
137  CreateWebContents(embedder_extension_id,
138                    embedder_process_id,
139                    create_params,
140                    base::Bind(&GuestViewBase::CompleteInit,
141                               AsWeakPtr(),
142                               embedder_extension_id,
143                               embedder_process_id,
144                               callback));
145}
146
147void GuestViewBase::InitWithWebContents(
148    const std::string& embedder_extension_id,
149    int embedder_render_process_id,
150    content::WebContents* guest_web_contents) {
151  DCHECK(guest_web_contents);
152  content::RenderProcessHost* embedder_render_process_host =
153      content::RenderProcessHost::FromID(embedder_render_process_id);
154
155  embedder_extension_id_ = embedder_extension_id;
156  embedder_render_process_id_ = embedder_render_process_host->GetID();
157  embedder_render_process_host->AddObserver(this);
158
159  WebContentsObserver::Observe(guest_web_contents);
160  guest_web_contents->SetDelegate(this);
161  webcontents_guestview_map.Get().insert(
162      std::make_pair(guest_web_contents, this));
163  GuestViewManager::FromBrowserContext(browser_context_)->
164      AddGuest(guest_instance_id_, guest_web_contents);
165
166  // Give the derived class an opportunity to perform additional initialization.
167  DidInitialize();
168}
169
170void GuestViewBase::SetAutoSize(bool enabled,
171                                const gfx::Size& min_size,
172                                const gfx::Size& max_size) {
173  min_auto_size_ = min_size;
174  min_auto_size_.SetToMin(max_size);
175  max_auto_size_ = max_size;
176  max_auto_size_.SetToMax(min_size);
177
178  enabled &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty() &&
179      IsAutoSizeSupported();
180  if (!enabled && !auto_size_enabled_)
181    return;
182
183  auto_size_enabled_ = enabled;
184
185  if (!attached())
186    return;
187
188  content::RenderViewHost* rvh = guest_web_contents()->GetRenderViewHost();
189  if (auto_size_enabled_) {
190    rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
191  } else {
192    rvh->DisableAutoResize(element_size_);
193    guest_size_ = element_size_;
194    GuestSizeChangedDueToAutoSize(guest_size_, element_size_);
195  }
196}
197
198// static
199void GuestViewBase::RegisterGuestViewType(
200    const std::string& view_type,
201    const GuestCreationCallback& callback) {
202  GuestViewCreationMap::iterator it =
203      guest_view_registry.Get().find(view_type);
204  DCHECK(it == guest_view_registry.Get().end());
205  guest_view_registry.Get()[view_type] = callback;
206}
207
208// static
209GuestViewBase* GuestViewBase::Create(
210    content::BrowserContext* browser_context,
211    int guest_instance_id,
212    const std::string& view_type) {
213  if (guest_view_registry.Get().empty())
214    RegisterGuestViewTypes();
215
216  GuestViewCreationMap::iterator it =
217      guest_view_registry.Get().find(view_type);
218  if (it == guest_view_registry.Get().end()) {
219    NOTREACHED();
220    return NULL;
221  }
222  return it->second.Run(browser_context, guest_instance_id);
223}
224
225// static
226GuestViewBase* GuestViewBase::FromWebContents(WebContents* web_contents) {
227  WebContentsGuestViewMap* guest_map = webcontents_guestview_map.Pointer();
228  WebContentsGuestViewMap::iterator it = guest_map->find(web_contents);
229  return it == guest_map->end() ? NULL : it->second;
230}
231
232// static
233GuestViewBase* GuestViewBase::From(int embedder_process_id,
234                                   int guest_instance_id) {
235  content::RenderProcessHost* host =
236      content::RenderProcessHost::FromID(embedder_process_id);
237  if (!host)
238    return NULL;
239
240  content::WebContents* guest_web_contents =
241      GuestViewManager::FromBrowserContext(host->GetBrowserContext())->
242          GetGuestByInstanceIDSafely(guest_instance_id, embedder_process_id);
243  if (!guest_web_contents)
244    return NULL;
245
246  return GuestViewBase::FromWebContents(guest_web_contents);
247}
248
249// static
250bool GuestViewBase::IsGuest(WebContents* web_contents) {
251  return !!GuestViewBase::FromWebContents(web_contents);
252}
253
254base::WeakPtr<GuestViewBase> GuestViewBase::AsWeakPtr() {
255  return weak_ptr_factory_.GetWeakPtr();
256}
257
258bool GuestViewBase::IsAutoSizeSupported() const {
259  return false;
260}
261
262bool GuestViewBase::IsDragAndDropEnabled() const {
263  return false;
264}
265
266void GuestViewBase::RenderProcessExited(content::RenderProcessHost* host,
267                                        base::ProcessHandle handle,
268                                        base::TerminationStatus status,
269                                        int exit_code) {
270  // GuestViewBase tracks the lifetime of its embedder render process until it
271  // is attached to a particular embedder WebContents. At that point, its
272  // lifetime is restricted in scope to the lifetime of its embedder
273  // WebContents.
274  CHECK(!attached());
275  CHECK_EQ(host->GetID(), embedder_render_process_id());
276
277  // This code path may be reached if the embedder WebContents is killed for
278  // whatever reason immediately after a called to GuestViewInternal.createGuest
279  // and before attaching the new guest to a frame.
280  Destroy();
281}
282
283void GuestViewBase::Destroy() {
284  DCHECK(guest_web_contents());
285  content::RenderProcessHost* host =
286      content::RenderProcessHost::FromID(embedder_render_process_id());
287  if (host)
288    host->RemoveObserver(this);
289  WillDestroy();
290  if (!destruction_callback_.is_null())
291    destruction_callback_.Run();
292
293  webcontents_guestview_map.Get().erase(guest_web_contents());
294  GuestViewManager::FromBrowserContext(browser_context_)->
295      RemoveGuest(guest_instance_id_);
296  pending_events_.clear();
297
298  delete guest_web_contents();
299}
300
301void GuestViewBase::DidAttach() {
302  // Give the derived class an opportunity to perform some actions.
303  DidAttachToEmbedder();
304
305  SendQueuedEvents();
306}
307
308void GuestViewBase::ElementSizeChanged(const gfx::Size& old_size,
309                                       const gfx::Size& new_size) {
310  element_size_ = new_size;
311}
312
313int GuestViewBase::GetGuestInstanceID() const {
314  return guest_instance_id_;
315}
316
317void GuestViewBase::GuestSizeChanged(const gfx::Size& old_size,
318                                     const gfx::Size& new_size) {
319  if (!auto_size_enabled_)
320    return;
321  guest_size_ = new_size;
322  GuestSizeChangedDueToAutoSize(old_size, new_size);
323}
324
325void GuestViewBase::SetOpener(GuestViewBase* guest) {
326  if (guest && guest->IsViewType(GetViewType())) {
327    opener_ = guest->AsWeakPtr();
328    return;
329  }
330  opener_ = base::WeakPtr<GuestViewBase>();
331}
332
333void GuestViewBase::RegisterDestructionCallback(
334    const DestructionCallback& callback) {
335  destruction_callback_ = callback;
336}
337
338void GuestViewBase::WillAttach(content::WebContents* embedder_web_contents,
339                               const base::DictionaryValue& extra_params) {
340  // After attachment, this GuestViewBase's lifetime is restricted to the
341  // lifetime of its embedder WebContents. Observing the RenderProcessHost
342  // of the embedder is no longer necessary.
343  embedder_web_contents->GetRenderProcessHost()->RemoveObserver(this);
344  embedder_web_contents_ = embedder_web_contents;
345  embedder_web_contents_observer_.reset(
346      new EmbedderWebContentsObserver(this));
347  extra_params.GetInteger(guestview::kParameterInstanceId, &view_instance_id_);
348  extra_params_.reset(extra_params.DeepCopy());
349
350  WillAttachToEmbedder();
351}
352
353void GuestViewBase::DidStopLoading(content::RenderViewHost* render_view_host) {
354  if (!IsDragAndDropEnabled()) {
355    const char script[] = "window.addEventListener('dragstart', function() { "
356                          "  window.event.preventDefault(); "
357                          "});";
358    render_view_host->GetMainFrame()->ExecuteJavaScript(
359        base::ASCIIToUTF16(script));
360  }
361  DidStopLoading();
362}
363
364void GuestViewBase::RenderViewReady() {
365  GuestReady();
366  content::RenderViewHost* rvh = guest_web_contents()->GetRenderViewHost();
367  if (auto_size_enabled_) {
368    rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
369  } else {
370    rvh->DisableAutoResize(element_size_);
371  }
372}
373
374void GuestViewBase::WebContentsDestroyed() {
375  GuestDestroyed();
376  delete this;
377}
378
379bool GuestViewBase::ShouldFocusPageAfterCrash() {
380  // Focus is managed elsewhere.
381  return false;
382}
383
384bool GuestViewBase::PreHandleGestureEvent(content::WebContents* source,
385                                         const blink::WebGestureEvent& event) {
386  return event.type == blink::WebGestureEvent::GesturePinchBegin ||
387      event.type == blink::WebGestureEvent::GesturePinchUpdate ||
388      event.type == blink::WebGestureEvent::GesturePinchEnd;
389}
390
391GuestViewBase::~GuestViewBase() {
392}
393
394void GuestViewBase::DispatchEventToEmbedder(Event* event) {
395  scoped_ptr<Event> event_ptr(event);
396
397  if (!attached()) {
398    pending_events_.push_back(linked_ptr<Event>(event_ptr.release()));
399    return;
400  }
401
402  EventFilteringInfo info;
403  info.SetInstanceID(view_instance_id_);
404  scoped_ptr<base::ListValue> args(new base::ListValue());
405  args->Append(event->GetArguments().release());
406
407  EventRouter::DispatchEvent(
408      embedder_web_contents_,
409      browser_context_,
410      embedder_extension_id_,
411      event->name(),
412      args.Pass(),
413      EventRouter::USER_GESTURE_UNKNOWN,
414      info);
415}
416
417void GuestViewBase::SendQueuedEvents() {
418  if (!attached())
419    return;
420  while (!pending_events_.empty()) {
421    linked_ptr<Event> event_ptr = pending_events_.front();
422    pending_events_.pop_front();
423    DispatchEventToEmbedder(event_ptr.release());
424  }
425}
426
427void GuestViewBase::CompleteInit(const std::string& embedder_extension_id,
428                                 int embedder_render_process_id,
429                                 const WebContentsCreatedCallback& callback,
430                                 content::WebContents* guest_web_contents) {
431  if (!guest_web_contents) {
432    // The derived class did not create a WebContents so this class serves no
433    // purpose. Let's self-destruct.
434    delete this;
435    callback.Run(NULL);
436    return;
437  }
438  InitWithWebContents(embedder_extension_id,
439                      embedder_render_process_id,
440                      guest_web_contents);
441  callback.Run(guest_web_contents);
442}
443
444// static
445void GuestViewBase::RegisterGuestViewTypes() {
446  ExtensionsAPIClient::Get()->RegisterGuestViewTypes();
447}
448
449}  // namespace extensions
450