OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" | 5 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" |
6 | 6 |
7 #include "base/lazy_instance.h" | 7 #include "base/lazy_instance.h" |
8 #include "chrome/browser/chrome_notification_types.h" | |
9 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" | |
10 #include "components/keyed_service/content/browser_context_dependency_manager.h" | 8 #include "components/keyed_service/content/browser_context_dependency_manager.h" |
11 #include "content/public/browser/browser_thread.h" | 9 #include "content/public/browser/browser_thread.h" |
12 #include "content/public/browser/notification_details.h" | 10 #include "content/public/browser/render_frame_host.h" |
13 #include "content/public/browser/notification_service.h" | |
14 #include "content/public/browser/notification_source.h" | |
15 #include "content/public/browser/render_view_host.h" | |
16 #include "content/public/browser/web_contents.h" | 11 #include "content/public/browser/web_contents.h" |
17 #include "content/public/browser/web_contents_observer.h" | 12 #include "content/public/browser/web_contents_observer.h" |
18 #include "extensions/browser/event_router.h" | 13 #include "extensions/browser/event_router.h" |
19 #include "extensions/browser/extension_registry.h" | 14 #include "extensions/browser/extension_registry.h" |
20 #include "extensions/common/extension.h" | 15 #include "extensions/common/extension.h" |
21 | 16 |
22 using content::BrowserThread; | 17 using content::BrowserThread; |
23 using extensions::tab_capture::TabCaptureState; | 18 using extensions::tab_capture::TabCaptureState; |
24 | 19 |
25 namespace extensions { | 20 namespace extensions { |
26 | 21 |
27 namespace tab_capture = api::tab_capture; | 22 namespace tab_capture = api::tab_capture; |
28 | 23 |
29 class FullscreenObserver : public content::WebContentsObserver { | 24 class FullscreenObserver : public content::WebContentsObserver { |
30 public: | 25 public: |
26 // Caller of this constructor must guarantee that |request->target_contents| | |
27 // is a valid pointer (i.e., it will be dereferenced). | |
ncarter (slow)
2014/07/10 01:17:51
Just say: "... is not NULL". The rest is implicit.
miu
2014/07/10 22:16:11
I think this was left over from earlier prototypin
| |
31 FullscreenObserver(TabCaptureRequest* request, | 28 FullscreenObserver(TabCaptureRequest* request, |
32 const TabCaptureRegistry* registry); | 29 const TabCaptureRegistry* registry); |
33 virtual ~FullscreenObserver() {} | 30 virtual ~FullscreenObserver() {} |
34 | 31 |
35 private: | 32 private: |
36 // content::WebContentsObserver implementation. | 33 // content::WebContentsObserver implementation. |
37 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE; | 34 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE; |
38 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE; | 35 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE; |
36 virtual void DidToggleFullscreenModeForTab(bool entered_fullscreen) OVERRIDE; | |
39 | 37 |
40 TabCaptureRequest* request_; | 38 TabCaptureRequest* request_; |
41 const TabCaptureRegistry* registry_; | 39 const TabCaptureRegistry* registry_; |
42 | 40 |
43 DISALLOW_COPY_AND_ASSIGN(FullscreenObserver); | 41 DISALLOW_COPY_AND_ASSIGN(FullscreenObserver); |
44 }; | 42 }; |
45 | 43 |
46 // Holds all the state related to a tab capture stream. | 44 // Holds all the state related to a tab capture stream. |
47 struct TabCaptureRequest { | 45 struct TabCaptureRequest { |
48 TabCaptureRequest(int render_process_id, | 46 TabCaptureRequest(content::WebContents* target_contents, |
49 int render_view_id, | |
50 const std::string& extension_id, | 47 const std::string& extension_id, |
51 int tab_id, | 48 int tab_id, |
52 TabCaptureState status); | 49 TabCaptureState status); |
53 ~TabCaptureRequest(); | 50 ~TabCaptureRequest(); |
54 | 51 |
55 const int render_process_id; | 52 content::WebContents* const target_contents; |
56 const int render_view_id; | |
57 const std::string extension_id; | 53 const std::string extension_id; |
58 const int tab_id; | 54 const int tab_id; |
59 TabCaptureState status; | 55 TabCaptureState status; |
60 TabCaptureState last_status; | 56 TabCaptureState last_status; |
61 bool fullscreen; | 57 bool fullscreen; |
62 scoped_ptr<FullscreenObserver> fullscreen_observer; | 58 scoped_ptr<FullscreenObserver> fullscreen_observer; |
63 }; | 59 }; |
64 | 60 |
65 FullscreenObserver::FullscreenObserver( | 61 FullscreenObserver::FullscreenObserver( |
66 TabCaptureRequest* request, | 62 TabCaptureRequest* request, |
67 const TabCaptureRegistry* registry) | 63 const TabCaptureRegistry* registry) |
68 : request_(request), | 64 : content::WebContentsObserver(request->target_contents), |
65 request_(request), | |
69 registry_(registry) { | 66 registry_(registry) { |
70 content::RenderViewHost* const rvh = | |
71 content::RenderViewHost::FromID(request->render_process_id, | |
72 request->render_view_id); | |
73 Observe(rvh ? content::WebContents::FromRenderViewHost(rvh) : NULL); | |
74 } | 67 } |
75 | 68 |
76 void FullscreenObserver::DidShowFullscreenWidget( | 69 void FullscreenObserver::DidShowFullscreenWidget( |
77 int routing_id) { | 70 int routing_id) { |
78 request_->fullscreen = true; | 71 request_->fullscreen = true; |
79 registry_->DispatchStatusChangeEvent(request_); | 72 registry_->DispatchStatusChangeEvent(request_); |
80 } | 73 } |
81 | 74 |
82 void FullscreenObserver::DidDestroyFullscreenWidget( | 75 void FullscreenObserver::DidDestroyFullscreenWidget( |
83 int routing_id) { | 76 int routing_id) { |
84 request_->fullscreen = false; | 77 request_->fullscreen = false; |
85 registry_->DispatchStatusChangeEvent(request_); | 78 registry_->DispatchStatusChangeEvent(request_); |
86 } | 79 } |
87 | 80 |
81 void FullscreenObserver::DidToggleFullscreenModeForTab( | |
82 bool entered_fullscreen) { | |
83 request_->fullscreen = entered_fullscreen; | |
84 registry_->DispatchStatusChangeEvent(request_); | |
85 } | |
86 | |
88 TabCaptureRequest::TabCaptureRequest( | 87 TabCaptureRequest::TabCaptureRequest( |
89 int render_process_id, | 88 content::WebContents* target_contents, |
90 int render_view_id, | |
91 const std::string& extension_id, | 89 const std::string& extension_id, |
92 const int tab_id, | 90 const int tab_id, |
93 TabCaptureState status) | 91 TabCaptureState status) |
94 : render_process_id(render_process_id), | 92 : target_contents(target_contents), |
95 render_view_id(render_view_id), | |
96 extension_id(extension_id), | 93 extension_id(extension_id), |
97 tab_id(tab_id), | 94 tab_id(tab_id), |
98 status(status), | 95 status(status), |
99 last_status(status), | 96 last_status(status), |
97 // TODO(miu): This initial value for |fullscreen| is a faulty assumption. | |
98 // http://crbug.com/350491 | |
100 fullscreen(false) { | 99 fullscreen(false) { |
101 } | 100 } |
102 | 101 |
103 TabCaptureRequest::~TabCaptureRequest() { | 102 TabCaptureRequest::~TabCaptureRequest() { |
104 } | 103 } |
105 | 104 |
106 TabCaptureRegistry::TabCaptureRegistry(content::BrowserContext* context) | 105 TabCaptureRegistry::TabCaptureRegistry(content::BrowserContext* context) |
107 : browser_context_(context), extension_registry_observer_(this) { | 106 : browser_context_(context), extension_registry_observer_(this) { |
108 MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this); | 107 MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this); |
109 registrar_.Add(this, | |
110 chrome::NOTIFICATION_FULLSCREEN_CHANGED, | |
111 content::NotificationService::AllSources()); | |
112 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); | 108 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); |
113 } | 109 } |
114 | 110 |
115 TabCaptureRegistry::~TabCaptureRegistry() { | 111 TabCaptureRegistry::~TabCaptureRegistry() { |
116 MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this); | 112 MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this); |
117 } | 113 } |
118 | 114 |
119 // static | 115 // static |
120 TabCaptureRegistry* TabCaptureRegistry::Get(content::BrowserContext* context) { | 116 TabCaptureRegistry* TabCaptureRegistry::Get(content::BrowserContext* context) { |
121 return BrowserContextKeyedAPIFactory<TabCaptureRegistry>::Get(context); | 117 return BrowserContextKeyedAPIFactory<TabCaptureRegistry>::Get(context); |
(...skipping 14 matching lines...) Expand all Loading... | |
136 RegistryCaptureInfo list; | 132 RegistryCaptureInfo list; |
137 for (ScopedVector<TabCaptureRequest>::const_iterator it = requests_.begin(); | 133 for (ScopedVector<TabCaptureRequest>::const_iterator it = requests_.begin(); |
138 it != requests_.end(); ++it) { | 134 it != requests_.end(); ++it) { |
139 if ((*it)->extension_id == extension_id) { | 135 if ((*it)->extension_id == extension_id) { |
140 list.push_back(std::make_pair((*it)->tab_id, (*it)->status)); | 136 list.push_back(std::make_pair((*it)->tab_id, (*it)->status)); |
141 } | 137 } |
142 } | 138 } |
143 return list; | 139 return list; |
144 } | 140 } |
145 | 141 |
146 void TabCaptureRegistry::Observe(int type, | |
147 const content::NotificationSource& source, | |
148 const content::NotificationDetails& details) { | |
149 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
150 DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type); | |
151 FullscreenController* fullscreen_controller = | |
152 content::Source<FullscreenController>(source).ptr(); | |
153 const bool is_fullscreen = *content::Details<bool>(details).ptr(); | |
154 for (ScopedVector<TabCaptureRequest>::iterator it = requests_.begin(); | |
155 it != requests_.end(); | |
156 ++it) { | |
157 // If we are exiting fullscreen mode, we only need to check if any of | |
158 // the requests had the fullscreen flag toggled previously. The | |
159 // fullscreen controller no longer has the reference to the fullscreen | |
160 // web_contents here. | |
161 if (!is_fullscreen) { | |
162 if ((*it)->fullscreen) { | |
163 (*it)->fullscreen = false; | |
164 DispatchStatusChangeEvent(*it); | |
165 break; | |
166 } | |
167 continue; | |
168 } | |
169 | |
170 // If we are entering fullscreen mode, find whether the web_contents we | |
171 // are capturing entered fullscreen mode. | |
172 content::RenderViewHost* const rvh = content::RenderViewHost::FromID( | |
173 (*it)->render_process_id, (*it)->render_view_id); | |
174 if (rvh && | |
175 fullscreen_controller->IsFullscreenForTabOrPending( | |
176 content::WebContents::FromRenderViewHost(rvh))) { | |
177 (*it)->fullscreen = true; | |
178 DispatchStatusChangeEvent(*it); | |
179 break; | |
180 } | |
181 } | |
182 } | |
183 | |
184 void TabCaptureRegistry::OnExtensionUnloaded( | 142 void TabCaptureRegistry::OnExtensionUnloaded( |
185 content::BrowserContext* browser_context, | 143 content::BrowserContext* browser_context, |
186 const Extension* extension, | 144 const Extension* extension, |
187 UnloadedExtensionInfo::Reason reason) { | 145 UnloadedExtensionInfo::Reason reason) { |
188 // Cleanup all the requested media streams for this extension. | 146 // Cleanup all the requested media streams for this extension. |
189 for (ScopedVector<TabCaptureRequest>::iterator it = requests_.begin(); | 147 for (ScopedVector<TabCaptureRequest>::iterator it = requests_.begin(); |
190 it != requests_.end();) { | 148 it != requests_.end();) { |
191 if ((*it)->extension_id == extension->id()) { | 149 if ((*it)->extension_id == extension->id()) { |
192 it = requests_.erase(it); | 150 it = requests_.erase(it); |
193 } else { | 151 } else { |
194 ++it; | 152 ++it; |
195 } | 153 } |
196 } | 154 } |
197 } | 155 } |
198 | 156 |
199 bool TabCaptureRegistry::AddRequest(int render_process_id, | 157 bool TabCaptureRegistry::AddRequest(content::WebContents* target_contents, |
200 int render_view_id, | |
201 const std::string& extension_id, | 158 const std::string& extension_id, |
202 int tab_id, | 159 int tab_id, |
203 TabCaptureState status) { | 160 TabCaptureState status) { |
204 TabCaptureRequest* request = FindCaptureRequest(render_process_id, | 161 TabCaptureRequest* const request = FindCaptureRequest(target_contents); |
205 render_view_id); | 162 |
206 // Currently, we do not allow multiple active captures for same tab. | 163 // Currently, we do not allow multiple active captures for same tab. |
207 if (request != NULL) { | 164 if (request != NULL) { |
208 if (request->status != tab_capture::TAB_CAPTURE_STATE_STOPPED && | 165 if (request->status != tab_capture::TAB_CAPTURE_STATE_STOPPED && |
209 request->status != tab_capture::TAB_CAPTURE_STATE_ERROR) { | 166 request->status != tab_capture::TAB_CAPTURE_STATE_ERROR) { |
210 return false; | 167 return false; |
211 } else { | 168 } else { |
212 DeleteCaptureRequest(render_process_id, render_view_id); | 169 // Delete the request. |
170 // TODO(miu): It's not clear that this code path will ever get executed | |
171 // and, in fact, it seems impossible that TabCaptureRequests will ever get | |
172 // deleted. Probably the root cause of http://crbug.com/338445. | |
ncarter (slow)
2014/07/10 01:17:51
If the entries of |requests_| are not properly cle
miu
2014/07/10 22:16:11
Done.
I used exactly your suggested approach of m
ncarter (slow)
2014/07/11 22:32:24
Wonderful, glad it worked out.
| |
173 for (ScopedVector<TabCaptureRequest>::iterator it = requests_.begin(); | |
174 it != requests_.end(); ++it) { | |
175 if ((*it)->target_contents == target_contents) { | |
176 requests_.erase(it); | |
177 break; | |
178 } | |
179 } | |
213 } | 180 } |
214 } | 181 } |
215 | 182 |
216 requests_.push_back(new TabCaptureRequest(render_process_id, | 183 requests_.push_back(new TabCaptureRequest(target_contents, |
217 render_view_id, | |
218 extension_id, | 184 extension_id, |
219 tab_id, | 185 tab_id, |
220 status)); | 186 status)); |
221 return true; | 187 return true; |
222 } | 188 } |
223 | 189 |
224 bool TabCaptureRegistry::VerifyRequest(int render_process_id, | 190 bool TabCaptureRegistry::VerifyRequest( |
225 int render_view_id) { | 191 const content::WebContents* target_contents, |
192 const std::string& extension_id) { | |
226 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 193 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
227 DVLOG(1) << "Verifying tabCapture request for " | 194 TabCaptureRequest* const request = FindCaptureRequest(target_contents); |
228 << render_process_id << ":" << render_view_id; | 195 // TODO(miu): We should probably also verify the origin, like the desktop |
229 // TODO(justinlin): Verify extension too. | 196 // capture API. http://crbug.com/163100 |
230 return (FindCaptureRequest(render_process_id, render_view_id) != NULL); | 197 return request && request->extension_id == extension_id; |
231 } | 198 } |
232 | 199 |
233 void TabCaptureRegistry::OnRequestUpdate( | 200 void TabCaptureRegistry::OnRequestUpdate( |
234 int render_process_id, | 201 int render_process_id, |
235 int render_view_id, | 202 int render_frame_id, |
236 const content::MediaStreamDevice& device, | 203 content::MediaStreamType stream_type, |
237 const content::MediaRequestState new_state) { | 204 const content::MediaRequestState new_state) { |
238 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 205 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
239 if (device.type != content::MEDIA_TAB_VIDEO_CAPTURE && | 206 if (stream_type != content::MEDIA_TAB_VIDEO_CAPTURE && |
240 device.type != content::MEDIA_TAB_AUDIO_CAPTURE) { | 207 stream_type != content::MEDIA_TAB_AUDIO_CAPTURE) { |
241 return; | 208 return; |
242 } | 209 } |
243 | 210 |
244 TabCaptureRequest* request = FindCaptureRequest(render_process_id, | 211 TabCaptureRequest* const request = FindCaptureRequest( |
245 render_view_id); | 212 content::WebContents::FromRenderFrameHost( |
213 content::RenderFrameHost::FromID( | |
214 render_process_id, render_frame_id))); | |
246 if (request == NULL) { | 215 if (request == NULL) { |
247 // TODO(justinlin): This can happen because the extension's renderer does | 216 // TODO(justinlin): This can happen because the extension's renderer does |
248 // not seem to always cleanup streams correctly. | 217 // not seem to always cleanup streams correctly. |
249 LOG(ERROR) << "Receiving updates for deleted capture request."; | 218 LOG(ERROR) << "Receiving updates for deleted capture request."; |
250 return; | 219 return; |
251 } | 220 } |
252 | 221 |
253 bool opening_stream = false; | 222 bool opening_stream = false; |
254 bool stopping_stream = false; | 223 bool stopping_stream = false; |
255 | 224 |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
321 scoped_ptr<base::ListValue> args(new base::ListValue()); | 290 scoped_ptr<base::ListValue> args(new base::ListValue()); |
322 args->Append(info->ToValue().release()); | 291 args->Append(info->ToValue().release()); |
323 scoped_ptr<Event> event(new Event(tab_capture::OnStatusChanged::kEventName, | 292 scoped_ptr<Event> event(new Event(tab_capture::OnStatusChanged::kEventName, |
324 args.Pass())); | 293 args.Pass())); |
325 event->restrict_to_browser_context = browser_context_; | 294 event->restrict_to_browser_context = browser_context_; |
326 | 295 |
327 router->DispatchEventToExtension(request->extension_id, event.Pass()); | 296 router->DispatchEventToExtension(request->extension_id, event.Pass()); |
328 } | 297 } |
329 | 298 |
330 TabCaptureRequest* TabCaptureRegistry::FindCaptureRequest( | 299 TabCaptureRequest* TabCaptureRegistry::FindCaptureRequest( |
331 int render_process_id, int render_view_id) const { | 300 const content::WebContents* target_contents) const { |
332 for (ScopedVector<TabCaptureRequest>::const_iterator it = requests_.begin(); | 301 for (ScopedVector<TabCaptureRequest>::const_iterator it = requests_.begin(); |
333 it != requests_.end(); ++it) { | 302 it != requests_.end(); ++it) { |
334 if ((*it)->render_process_id == render_process_id && | 303 if ((*it)->target_contents == target_contents) |
335 (*it)->render_view_id == render_view_id) { | |
336 return *it; | 304 return *it; |
337 } | |
338 } | 305 } |
339 return NULL; | 306 return NULL; |
340 } | 307 } |
341 | 308 |
342 void TabCaptureRegistry::DeleteCaptureRequest(int render_process_id, | |
343 int render_view_id) { | |
344 for (ScopedVector<TabCaptureRequest>::iterator it = requests_.begin(); | |
345 it != requests_.end(); ++it) { | |
346 if ((*it)->render_process_id == render_process_id && | |
347 (*it)->render_view_id == render_view_id) { | |
348 requests_.erase(it); | |
349 return; | |
350 } | |
351 } | |
352 } | |
353 | |
354 } // namespace extensions | 309 } // namespace extensions |
OLD | NEW |