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