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