Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / guest_view / guest_view_base.cc
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   content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
391   if (auto_size_enabled_) {
392     rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
393   } else {
394     rvh->DisableAutoResize(element_size_);
395   }
396 }
397
398 void GuestViewBase::WebContentsDestroyed() {
399   GuestDestroyed();
400   delete this;
401 }
402
403 void GuestViewBase::ActivateContents(WebContents* web_contents) {
404   if (!attached() || !embedder_web_contents()->GetDelegate())
405     return;
406
407   embedder_web_contents()->GetDelegate()->ActivateContents(
408       embedder_web_contents());
409 }
410
411 void GuestViewBase::DeactivateContents(WebContents* web_contents) {
412   if (!attached() || !embedder_web_contents()->GetDelegate())
413     return;
414
415   embedder_web_contents()->GetDelegate()->DeactivateContents(
416       embedder_web_contents());
417 }
418
419 void GuestViewBase::RunFileChooser(WebContents* web_contents,
420                                    const content::FileChooserParams& params) {
421   if (!attached() || !embedder_web_contents()->GetDelegate())
422     return;
423
424   embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params);
425 }
426
427 bool GuestViewBase::ShouldFocusPageAfterCrash() {
428   // Focus is managed elsewhere.
429   return false;
430 }
431
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;
437 }
438
439 GuestViewBase::~GuestViewBase() {
440 }
441
442 void GuestViewBase::DispatchEventToEmbedder(Event* event) {
443   scoped_ptr<Event> event_ptr(event);
444
445   if (!attached()) {
446     pending_events_.push_back(linked_ptr<Event>(event_ptr.release()));
447     return;
448   }
449
450   EventFilteringInfo info;
451   info.SetInstanceID(view_instance_id_);
452   scoped_ptr<base::ListValue> args(new base::ListValue());
453   args->Append(event->GetArguments().release());
454
455   EventRouter::DispatchEvent(
456       embedder_web_contents_,
457       browser_context_,
458       embedder_extension_id_,
459       event->name(),
460       args.Pass(),
461       EventRouter::USER_GESTURE_UNKNOWN,
462       info);
463 }
464
465 void GuestViewBase::SendQueuedEvents() {
466   if (!attached())
467     return;
468   while (!pending_events_.empty()) {
469     linked_ptr<Event> event_ptr = pending_events_.front();
470     pending_events_.pop_front();
471     DispatchEventToEmbedder(event_ptr.release());
472   }
473 }
474
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.
482     delete this;
483     callback.Run(NULL);
484     return;
485   }
486   InitWithWebContents(embedder_extension_id,
487                       embedder_render_process_id,
488                       guest_web_contents);
489   callback.Run(guest_web_contents);
490 }
491
492 // static
493 void GuestViewBase::RegisterGuestViewTypes() {
494   AppViewGuest::Register();
495   ExtensionOptionsGuest::Register();
496   MimeHandlerViewGuest::Register();
497   WebViewGuest::Register();
498 }
499
500 }  // namespace extensions