OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/extensions/extension_host.h" | |
6 | |
7 #include <list> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/logging.h" | |
11 #include "base/memory/singleton.h" | |
12 #include "base/memory/weak_ptr.h" | |
13 #include "base/message_loop/message_loop.h" | |
14 #include "base/metrics/histogram.h" | |
15 #include "base/strings/string_util.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "chrome/browser/chrome_notification_types.h" | |
18 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h" | |
19 #include "content/public/browser/browser_context.h" | |
20 #include "content/public/browser/content_browser_client.h" | |
21 #include "content/public/browser/native_web_keyboard_event.h" | |
22 #include "content/public/browser/notification_service.h" | |
23 #include "content/public/browser/notification_source.h" | |
24 #include "content/public/browser/notification_types.h" | |
25 #include "content/public/browser/render_process_host.h" | |
26 #include "content/public/browser/render_view_host.h" | |
27 #include "content/public/browser/render_widget_host_view.h" | |
28 #include "content/public/browser/site_instance.h" | |
29 #include "content/public/browser/web_contents.h" | |
30 #include "extensions/browser/event_router.h" | |
31 #include "extensions/browser/extension_error.h" | |
32 #include "extensions/browser/extension_host_delegate.h" | |
33 #include "extensions/browser/extension_system.h" | |
34 #include "extensions/browser/extensions_browser_client.h" | |
35 #include "extensions/browser/process_manager.h" | |
36 #include "extensions/browser/runtime_data.h" | |
37 #include "extensions/browser/view_type_utils.h" | |
38 #include "extensions/common/extension.h" | |
39 #include "extensions/common/extension_messages.h" | |
40 #include "extensions/common/extension_urls.h" | |
41 #include "extensions/common/feature_switch.h" | |
42 #include "extensions/common/manifest_handlers/background_info.h" | |
43 #include "third_party/WebKit/public/web/WebInputEvent.h" | |
44 #include "ui/base/l10n/l10n_util.h" | |
45 #include "ui/base/window_open_disposition.h" | |
46 | |
47 using content::BrowserContext; | |
48 using content::OpenURLParams; | |
49 using content::RenderProcessHost; | |
50 using content::RenderViewHost; | |
51 using content::SiteInstance; | |
52 using content::WebContents; | |
53 | |
54 namespace extensions { | |
55 | |
56 // Helper class that rate-limits the creation of renderer processes for | |
57 // ExtensionHosts, to avoid blocking the UI. | |
58 class ExtensionHost::ProcessCreationQueue { | |
59 public: | |
60 static ProcessCreationQueue* GetInstance() { | |
61 return Singleton<ProcessCreationQueue>::get(); | |
62 } | |
63 | |
64 // Add a host to the queue for RenderView creation. | |
65 void CreateSoon(ExtensionHost* host) { | |
66 queue_.push_back(host); | |
67 PostTask(); | |
68 } | |
69 | |
70 // Remove a host from the queue (in case it's being deleted). | |
71 void Remove(ExtensionHost* host) { | |
72 Queue::iterator it = std::find(queue_.begin(), queue_.end(), host); | |
73 if (it != queue_.end()) | |
74 queue_.erase(it); | |
75 } | |
76 | |
77 private: | |
78 friend class Singleton<ProcessCreationQueue>; | |
79 friend struct DefaultSingletonTraits<ProcessCreationQueue>; | |
80 ProcessCreationQueue() | |
81 : pending_create_(false), | |
82 ptr_factory_(this) {} | |
83 | |
84 // Queue up a delayed task to process the next ExtensionHost in the queue. | |
85 void PostTask() { | |
86 if (!pending_create_) { | |
87 base::MessageLoop::current()->PostTask(FROM_HERE, | |
88 base::Bind(&ProcessCreationQueue::ProcessOneHost, | |
89 ptr_factory_.GetWeakPtr())); | |
90 pending_create_ = true; | |
91 } | |
92 } | |
93 | |
94 // Create the RenderView for the next host in the queue. | |
95 void ProcessOneHost() { | |
96 pending_create_ = false; | |
97 if (queue_.empty()) | |
98 return; // can happen on shutdown | |
99 | |
100 queue_.front()->CreateRenderViewNow(); | |
101 queue_.pop_front(); | |
102 | |
103 if (!queue_.empty()) | |
104 PostTask(); | |
105 } | |
106 | |
107 typedef std::list<ExtensionHost*> Queue; | |
108 Queue queue_; | |
109 bool pending_create_; | |
110 base::WeakPtrFactory<ProcessCreationQueue> ptr_factory_; | |
111 }; | |
112 | |
113 //////////////// | |
114 // ExtensionHost | |
115 | |
116 ExtensionHost::ExtensionHost(const Extension* extension, | |
117 SiteInstance* site_instance, | |
118 const GURL& url, | |
119 ViewType host_type) | |
120 : delegate_(ExtensionsBrowserClient::Get()->CreateExtensionHostDelegate()), | |
121 extension_(extension), | |
122 extension_id_(extension->id()), | |
123 browser_context_(site_instance->GetBrowserContext()), | |
124 render_view_host_(NULL), | |
125 did_stop_loading_(false), | |
126 document_element_available_(false), | |
127 initial_url_(url), | |
128 extension_function_dispatcher_(browser_context_, this), | |
129 extension_host_type_(host_type) { | |
130 // Not used for panels, see PanelHost. | |
131 DCHECK(host_type == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE || | |
132 host_type == VIEW_TYPE_EXTENSION_DIALOG || | |
133 host_type == VIEW_TYPE_EXTENSION_INFOBAR || | |
134 host_type == VIEW_TYPE_EXTENSION_POPUP); | |
135 host_contents_.reset(WebContents::Create( | |
136 WebContents::CreateParams(browser_context_, site_instance))), | |
137 content::WebContentsObserver::Observe(host_contents_.get()); | |
138 host_contents_->SetDelegate(this); | |
139 SetViewType(host_contents_.get(), host_type); | |
140 | |
141 render_view_host_ = host_contents_->GetRenderViewHost(); | |
142 | |
143 // Listen for when an extension is unloaded from the same profile, as it may | |
144 // be the same extension that this points to. | |
145 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, | |
146 content::Source<BrowserContext>(browser_context_)); | |
147 | |
148 // Set up web contents observers and pref observers. | |
149 delegate_->OnExtensionHostCreated(host_contents()); | |
150 } | |
151 | |
152 ExtensionHost::~ExtensionHost() { | |
153 if (extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE && | |
154 extension_ && BackgroundInfo::HasLazyBackgroundPage(extension_)) { | |
155 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageActiveTime", | |
156 since_created_.Elapsed()); | |
157 } | |
158 content::NotificationService::current()->Notify( | |
159 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED, | |
160 content::Source<BrowserContext>(browser_context_), | |
161 content::Details<ExtensionHost>(this)); | |
162 ProcessCreationQueue::GetInstance()->Remove(this); | |
163 } | |
164 | |
165 content::RenderProcessHost* ExtensionHost::render_process_host() const { | |
166 return render_view_host()->GetProcess(); | |
167 } | |
168 | |
169 RenderViewHost* ExtensionHost::render_view_host() const { | |
170 // TODO(mpcomplete): This can be NULL. How do we handle that? | |
171 return render_view_host_; | |
172 } | |
173 | |
174 bool ExtensionHost::IsRenderViewLive() const { | |
175 return render_view_host()->IsRenderViewLive(); | |
176 } | |
177 | |
178 void ExtensionHost::CreateRenderViewSoon() { | |
179 if ((render_process_host() && render_process_host()->HasConnection())) { | |
180 // If the process is already started, go ahead and initialize the RenderView | |
181 // synchronously. The process creation is the real meaty part that we want | |
182 // to defer. | |
183 CreateRenderViewNow(); | |
184 } else { | |
185 ProcessCreationQueue::GetInstance()->CreateSoon(this); | |
186 } | |
187 } | |
188 | |
189 void ExtensionHost::CreateRenderViewNow() { | |
190 LoadInitialURL(); | |
191 if (IsBackgroundPage()) { | |
192 DCHECK(IsRenderViewLive()); | |
193 // Connect orphaned dev-tools instances. | |
194 delegate_->OnRenderViewCreatedForBackgroundPage(this); | |
195 } | |
196 } | |
197 | |
198 const GURL& ExtensionHost::GetURL() const { | |
199 return host_contents()->GetURL(); | |
200 } | |
201 | |
202 void ExtensionHost::LoadInitialURL() { | |
203 host_contents_->GetController().LoadURL( | |
204 initial_url_, content::Referrer(), content::PAGE_TRANSITION_LINK, | |
205 std::string()); | |
206 } | |
207 | |
208 bool ExtensionHost::IsBackgroundPage() const { | |
209 DCHECK(extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | |
210 return true; | |
211 } | |
212 | |
213 void ExtensionHost::Close() { | |
214 content::NotificationService::current()->Notify( | |
215 chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, | |
216 content::Source<BrowserContext>(browser_context_), | |
217 content::Details<ExtensionHost>(this)); | |
218 } | |
219 | |
220 void ExtensionHost::Observe(int type, | |
221 const content::NotificationSource& source, | |
222 const content::NotificationDetails& details) { | |
223 switch (type) { | |
224 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: | |
225 // The extension object will be deleted after this notification has been | |
226 // sent. NULL it out so that dirty pointer issues don't arise in cases | |
227 // when multiple ExtensionHost objects pointing to the same Extension are | |
228 // present. | |
229 if (extension_ == content::Details<UnloadedExtensionInfo>(details)-> | |
230 extension) { | |
231 extension_ = NULL; | |
232 } | |
233 break; | |
234 default: | |
235 NOTREACHED() << "Unexpected notification sent."; | |
236 break; | |
237 } | |
238 } | |
239 | |
240 void ExtensionHost::RenderProcessGone(base::TerminationStatus status) { | |
241 // During browser shutdown, we may use sudden termination on an extension | |
242 // process, so it is expected to lose our connection to the render view. | |
243 // Do nothing. | |
244 RenderProcessHost* process_host = host_contents_->GetRenderProcessHost(); | |
245 if (process_host && process_host->FastShutdownStarted()) | |
246 return; | |
247 | |
248 // In certain cases, multiple ExtensionHost objects may have pointed to | |
249 // the same Extension at some point (one with a background page and a | |
250 // popup, for example). When the first ExtensionHost goes away, the extension | |
251 // is unloaded, and any other host that pointed to that extension will have | |
252 // its pointer to it NULLed out so that any attempt to unload a dirty pointer | |
253 // will be averted. | |
254 if (!extension_) | |
255 return; | |
256 | |
257 // TODO(aa): This is suspicious. There can be multiple views in an extension, | |
258 // and they aren't all going to use ExtensionHost. This should be in someplace | |
259 // more central, like EPM maybe. | |
260 content::NotificationService::current()->Notify( | |
261 chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, | |
262 content::Source<BrowserContext>(browser_context_), | |
263 content::Details<ExtensionHost>(this)); | |
264 } | |
265 | |
266 void ExtensionHost::DidStopLoading(content::RenderViewHost* render_view_host) { | |
267 bool notify = !did_stop_loading_; | |
268 did_stop_loading_ = true; | |
269 OnDidStopLoading(); | |
270 if (notify) { | |
271 if (extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { | |
272 if (extension_ && BackgroundInfo::HasLazyBackgroundPage(extension_)) { | |
273 UMA_HISTOGRAM_TIMES("Extensions.EventPageLoadTime", | |
274 since_created_.Elapsed()); | |
275 } else { | |
276 UMA_HISTOGRAM_TIMES("Extensions.BackgroundPageLoadTime", | |
277 since_created_.Elapsed()); | |
278 } | |
279 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_DIALOG) { | |
280 UMA_HISTOGRAM_TIMES("Extensions.DialogLoadTime", | |
281 since_created_.Elapsed()); | |
282 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP) { | |
283 UMA_HISTOGRAM_TIMES("Extensions.PopupLoadTime", | |
284 since_created_.Elapsed()); | |
285 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_INFOBAR) { | |
286 UMA_HISTOGRAM_TIMES("Extensions.InfobarLoadTime", | |
287 since_created_.Elapsed()); | |
288 } | |
289 | |
290 // Send the notification last, because it might result in this being | |
291 // deleted. | |
292 content::NotificationService::current()->Notify( | |
293 chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, | |
294 content::Source<BrowserContext>(browser_context_), | |
295 content::Details<ExtensionHost>(this)); | |
296 } | |
297 } | |
298 | |
299 void ExtensionHost::OnDidStopLoading() { | |
300 DCHECK(extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | |
301 // Nothing to do for background pages. | |
302 } | |
303 | |
304 void ExtensionHost::DocumentAvailableInMainFrame() { | |
305 // If the document has already been marked as available for this host, then | |
306 // bail. No need for the redundant setup. http://crbug.com/31170 | |
307 if (document_element_available_) | |
308 return; | |
309 document_element_available_ = true; | |
310 OnDocumentAvailable(); | |
311 } | |
312 | |
313 void ExtensionHost::OnDocumentAvailable() { | |
314 DCHECK(extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | |
315 ExtensionSystem::Get(browser_context_) | |
316 ->runtime_data() | |
317 ->SetBackgroundPageReady(extension_, true); | |
318 content::NotificationService::current()->Notify( | |
319 chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY, | |
320 content::Source<const Extension>(extension_), | |
321 content::NotificationService::NoDetails()); | |
322 } | |
323 | |
324 void ExtensionHost::CloseContents(WebContents* contents) { | |
325 Close(); | |
326 } | |
327 | |
328 bool ExtensionHost::OnMessageReceived(const IPC::Message& message) { | |
329 bool handled = true; | |
330 IPC_BEGIN_MESSAGE_MAP(ExtensionHost, message) | |
331 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest) | |
332 IPC_MESSAGE_HANDLER(ExtensionHostMsg_EventAck, OnEventAck) | |
333 IPC_MESSAGE_HANDLER(ExtensionHostMsg_IncrementLazyKeepaliveCount, | |
334 OnIncrementLazyKeepaliveCount) | |
335 IPC_MESSAGE_HANDLER(ExtensionHostMsg_DecrementLazyKeepaliveCount, | |
336 OnDecrementLazyKeepaliveCount) | |
337 IPC_MESSAGE_UNHANDLED(handled = false) | |
338 IPC_END_MESSAGE_MAP() | |
339 return handled; | |
340 } | |
341 | |
342 void ExtensionHost::OnRequest(const ExtensionHostMsg_Request_Params& params) { | |
343 extension_function_dispatcher_.Dispatch(params, render_view_host()); | |
344 } | |
345 | |
346 void ExtensionHost::OnEventAck() { | |
347 EventRouter* router = ExtensionSystem::Get(browser_context_)->event_router(); | |
348 if (router) | |
349 router->OnEventAck(browser_context_, extension_id()); | |
350 } | |
351 | |
352 void ExtensionHost::OnIncrementLazyKeepaliveCount() { | |
353 ProcessManager* pm = ExtensionSystem::Get( | |
354 browser_context_)->process_manager(); | |
355 if (pm) | |
356 pm->IncrementLazyKeepaliveCount(extension()); | |
357 } | |
358 | |
359 void ExtensionHost::OnDecrementLazyKeepaliveCount() { | |
360 ProcessManager* pm = ExtensionSystem::Get( | |
361 browser_context_)->process_manager(); | |
362 if (pm) | |
363 pm->DecrementLazyKeepaliveCount(extension()); | |
364 } | |
365 | |
366 // content::WebContentsObserver | |
367 | |
368 void ExtensionHost::RenderViewCreated(RenderViewHost* render_view_host) { | |
369 render_view_host_ = render_view_host; | |
370 } | |
371 | |
372 void ExtensionHost::RenderViewDeleted(RenderViewHost* render_view_host) { | |
373 // If our RenderViewHost is deleted, fall back to the host_contents' current | |
374 // RVH. There is sometimes a small gap between the pending RVH being deleted | |
375 // and RenderViewCreated being called, so we update it here. | |
376 if (render_view_host == render_view_host_) | |
377 render_view_host_ = host_contents_->GetRenderViewHost(); | |
378 } | |
379 | |
380 content::JavaScriptDialogManager* ExtensionHost::GetJavaScriptDialogManager() { | |
381 return delegate_->GetJavaScriptDialogManager(); | |
382 } | |
383 | |
384 void ExtensionHost::AddNewContents(WebContents* source, | |
385 WebContents* new_contents, | |
386 WindowOpenDisposition disposition, | |
387 const gfx::Rect& initial_pos, | |
388 bool user_gesture, | |
389 bool* was_blocked) { | |
390 // First, if the creating extension view was associated with a tab contents, | |
391 // use that tab content's delegate. We must be careful here that the | |
392 // associated tab contents has the same profile as the new tab contents. In | |
393 // the case of extensions in 'spanning' incognito mode, they can mismatch. | |
394 // We don't want to end up putting a normal tab into an incognito window, or | |
395 // vice versa. | |
396 // Note that we don't do this for popup windows, because we need to associate | |
397 // those with their extension_app_id. | |
398 if (disposition != NEW_POPUP) { | |
399 WebContents* associated_contents = GetAssociatedWebContents(); | |
400 if (associated_contents && | |
401 associated_contents->GetBrowserContext() == | |
402 new_contents->GetBrowserContext()) { | |
403 WebContentsDelegate* delegate = associated_contents->GetDelegate(); | |
404 if (delegate) { | |
405 delegate->AddNewContents( | |
406 associated_contents, new_contents, disposition, initial_pos, | |
407 user_gesture, was_blocked); | |
408 return; | |
409 } | |
410 } | |
411 } | |
412 | |
413 delegate_->CreateTab( | |
414 new_contents, extension_id_, disposition, initial_pos, user_gesture); | |
415 } | |
416 | |
417 void ExtensionHost::RenderViewReady() { | |
418 content::NotificationService::current()->Notify( | |
419 chrome::NOTIFICATION_EXTENSION_HOST_CREATED, | |
420 content::Source<BrowserContext>(browser_context_), | |
421 content::Details<ExtensionHost>(this)); | |
422 } | |
423 | |
424 void ExtensionHost::RequestMediaAccessPermission( | |
425 content::WebContents* web_contents, | |
426 const content::MediaStreamRequest& request, | |
427 const content::MediaResponseCallback& callback) { | |
428 delegate_->ProcessMediaAccessRequest( | |
429 web_contents, request, callback, extension()); | |
430 } | |
431 | |
432 bool ExtensionHost::PreHandleGestureEvent( | |
433 content::WebContents* source, | |
434 const blink::WebGestureEvent& event) { | |
435 // Disable pinch zooming. | |
436 return event.type == blink::WebGestureEvent::GesturePinchBegin || | |
437 event.type == blink::WebGestureEvent::GesturePinchUpdate || | |
438 event.type == blink::WebGestureEvent::GesturePinchEnd; | |
439 } | |
440 | |
441 } // namespace extensions | |
OLD | NEW |