Enable Enterprise enrollment on desktop builds.
[chromium-blink-merge.git] / chrome / browser / extensions / api / tab_capture / tab_capture_registry.cc
blob09441cac28cbf53844bed3f76138952333490496
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.
5 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
7 #include "base/lazy_instance.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
11 #include "components/keyed_service/content/browser_context_dependency_manager.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/notification_details.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_source.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/browser/web_contents_observer.h"
19 #include "extensions/browser/event_router.h"
20 #include "extensions/common/extension.h"
22 using content::BrowserThread;
23 using extensions::TabCaptureRegistry;
24 using extensions::tab_capture::TabCaptureState;
26 namespace extensions {
28 namespace tab_capture = api::tab_capture;
30 class FullscreenObserver : public content::WebContentsObserver {
31 public:
32 FullscreenObserver(TabCaptureRequest* request,
33 const TabCaptureRegistry* registry);
34 virtual ~FullscreenObserver() {}
36 private:
37 // content::WebContentsObserver implementation.
38 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE;
39 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE;
41 TabCaptureRequest* request_;
42 const TabCaptureRegistry* registry_;
44 DISALLOW_COPY_AND_ASSIGN(FullscreenObserver);
47 // Holds all the state related to a tab capture stream.
48 struct TabCaptureRequest {
49 TabCaptureRequest(int render_process_id,
50 int render_view_id,
51 const std::string& extension_id,
52 int tab_id,
53 TabCaptureState status);
54 ~TabCaptureRequest();
56 const int render_process_id;
57 const int render_view_id;
58 const std::string extension_id;
59 const int tab_id;
60 TabCaptureState status;
61 TabCaptureState last_status;
62 bool fullscreen;
63 scoped_ptr<FullscreenObserver> fullscreen_observer;
66 FullscreenObserver::FullscreenObserver(
67 TabCaptureRequest* request,
68 const TabCaptureRegistry* registry)
69 : request_(request),
70 registry_(registry) {
71 content::RenderViewHost* const rvh =
72 content::RenderViewHost::FromID(request->render_process_id,
73 request->render_view_id);
74 Observe(rvh ? content::WebContents::FromRenderViewHost(rvh) : NULL);
77 void FullscreenObserver::DidShowFullscreenWidget(
78 int routing_id) {
79 request_->fullscreen = true;
80 registry_->DispatchStatusChangeEvent(request_);
83 void FullscreenObserver::DidDestroyFullscreenWidget(
84 int routing_id) {
85 request_->fullscreen = false;
86 registry_->DispatchStatusChangeEvent(request_);
89 TabCaptureRequest::TabCaptureRequest(
90 int render_process_id,
91 int render_view_id,
92 const std::string& extension_id,
93 const int tab_id,
94 TabCaptureState status)
95 : render_process_id(render_process_id),
96 render_view_id(render_view_id),
97 extension_id(extension_id),
98 tab_id(tab_id),
99 status(status),
100 last_status(status),
101 fullscreen(false) {
104 TabCaptureRequest::~TabCaptureRequest() {
107 TabCaptureRegistry::TabCaptureRegistry(content::BrowserContext* context)
108 : profile_(Profile::FromBrowserContext(context)) {
109 MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this);
110 registrar_.Add(this,
111 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
112 content::Source<Profile>(profile_));
113 registrar_.Add(this,
114 chrome::NOTIFICATION_FULLSCREEN_CHANGED,
115 content::NotificationService::AllSources());
118 TabCaptureRegistry::~TabCaptureRegistry() {
119 MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this);
122 // static
123 TabCaptureRegistry* TabCaptureRegistry::Get(content::BrowserContext* context) {
124 return BrowserContextKeyedAPIFactory<TabCaptureRegistry>::Get(context);
127 static base::LazyInstance<BrowserContextKeyedAPIFactory<TabCaptureRegistry> >
128 g_factory = LAZY_INSTANCE_INITIALIZER;
130 // static
131 BrowserContextKeyedAPIFactory<TabCaptureRegistry>*
132 TabCaptureRegistry::GetFactoryInstance() {
133 return g_factory.Pointer();
136 const TabCaptureRegistry::RegistryCaptureInfo
137 TabCaptureRegistry::GetCapturedTabs(const std::string& extension_id) const {
138 DCHECK_CURRENTLY_ON(BrowserThread::UI);
139 RegistryCaptureInfo list;
140 for (ScopedVector<TabCaptureRequest>::const_iterator it = requests_.begin();
141 it != requests_.end(); ++it) {
142 if ((*it)->extension_id == extension_id) {
143 list.push_back(std::make_pair((*it)->tab_id, (*it)->status));
146 return list;
149 void TabCaptureRegistry::Observe(int type,
150 const content::NotificationSource& source,
151 const content::NotificationDetails& details) {
152 DCHECK_CURRENTLY_ON(BrowserThread::UI);
153 switch (type) {
154 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
155 // Cleanup all the requested media streams for this extension.
156 const std::string& extension_id =
157 content::Details<extensions::UnloadedExtensionInfo>(details)->
158 extension->id();
159 for (ScopedVector<TabCaptureRequest>::iterator it = requests_.begin();
160 it != requests_.end();) {
161 if ((*it)->extension_id == extension_id) {
162 it = requests_.erase(it);
163 } else {
164 ++it;
167 break;
169 case chrome::NOTIFICATION_FULLSCREEN_CHANGED: {
170 FullscreenController* fullscreen_controller =
171 content::Source<FullscreenController>(source).ptr();
172 const bool is_fullscreen = *content::Details<bool>(details).ptr();
173 for (ScopedVector<TabCaptureRequest>::iterator it = requests_.begin();
174 it != requests_.end(); ++it) {
175 // If we are exiting fullscreen mode, we only need to check if any of
176 // the requests had the fullscreen flag toggled previously. The
177 // fullscreen controller no longer has the reference to the fullscreen
178 // web_contents here.
179 if (!is_fullscreen) {
180 if ((*it)->fullscreen) {
181 (*it)->fullscreen = false;
182 DispatchStatusChangeEvent(*it);
183 break;
185 continue;
188 // If we are entering fullscreen mode, find whether the web_contents we
189 // are capturing entered fullscreen mode.
190 content::RenderViewHost* const rvh =
191 content::RenderViewHost::FromID((*it)->render_process_id,
192 (*it)->render_view_id);
193 if (rvh && fullscreen_controller->IsFullscreenForTabOrPending(
194 content::WebContents::FromRenderViewHost(rvh))) {
195 (*it)->fullscreen = true;
196 DispatchStatusChangeEvent(*it);
197 break;
200 break;
205 bool TabCaptureRegistry::AddRequest(int render_process_id,
206 int render_view_id,
207 const std::string& extension_id,
208 int tab_id,
209 TabCaptureState status) {
210 TabCaptureRequest* request = FindCaptureRequest(render_process_id,
211 render_view_id);
212 // Currently, we do not allow multiple active captures for same tab.
213 if (request != NULL) {
214 if (request->status != tab_capture::TAB_CAPTURE_STATE_STOPPED &&
215 request->status != tab_capture::TAB_CAPTURE_STATE_ERROR) {
216 return false;
217 } else {
218 DeleteCaptureRequest(render_process_id, render_view_id);
222 requests_.push_back(new TabCaptureRequest(render_process_id,
223 render_view_id,
224 extension_id,
225 tab_id,
226 status));
227 return true;
230 bool TabCaptureRegistry::VerifyRequest(int render_process_id,
231 int render_view_id) {
232 DCHECK_CURRENTLY_ON(BrowserThread::UI);
233 DVLOG(1) << "Verifying tabCapture request for "
234 << render_process_id << ":" << render_view_id;
235 // TODO(justinlin): Verify extension too.
236 return (FindCaptureRequest(render_process_id, render_view_id) != NULL);
239 void TabCaptureRegistry::OnRequestUpdate(
240 int render_process_id,
241 int render_view_id,
242 const content::MediaStreamDevice& device,
243 const content::MediaRequestState new_state) {
244 DCHECK_CURRENTLY_ON(BrowserThread::UI);
245 if (device.type != content::MEDIA_TAB_VIDEO_CAPTURE &&
246 device.type != content::MEDIA_TAB_AUDIO_CAPTURE) {
247 return;
250 TabCaptureRequest* request = FindCaptureRequest(render_process_id,
251 render_view_id);
252 if (request == NULL) {
253 // TODO(justinlin): This can happen because the extension's renderer does
254 // not seem to always cleanup streams correctly.
255 LOG(ERROR) << "Receiving updates for deleted capture request.";
256 return;
259 bool opening_stream = false;
260 bool stopping_stream = false;
262 TabCaptureState next_state = tab_capture::TAB_CAPTURE_STATE_NONE;
263 switch (new_state) {
264 case content::MEDIA_REQUEST_STATE_PENDING_APPROVAL:
265 next_state = tab_capture::TAB_CAPTURE_STATE_PENDING;
266 break;
267 case content::MEDIA_REQUEST_STATE_DONE:
268 opening_stream = true;
269 next_state = tab_capture::TAB_CAPTURE_STATE_ACTIVE;
270 break;
271 case content::MEDIA_REQUEST_STATE_CLOSING:
272 stopping_stream = true;
273 next_state = tab_capture::TAB_CAPTURE_STATE_STOPPED;
274 break;
275 case content::MEDIA_REQUEST_STATE_ERROR:
276 stopping_stream = true;
277 next_state = tab_capture::TAB_CAPTURE_STATE_ERROR;
278 break;
279 case content::MEDIA_REQUEST_STATE_OPENING:
280 return;
281 case content::MEDIA_REQUEST_STATE_REQUESTED:
282 case content::MEDIA_REQUEST_STATE_NOT_REQUESTED:
283 NOTREACHED();
284 return;
287 if (next_state == tab_capture::TAB_CAPTURE_STATE_PENDING &&
288 request->status != tab_capture::TAB_CAPTURE_STATE_PENDING &&
289 request->status != tab_capture::TAB_CAPTURE_STATE_NONE &&
290 request->status != tab_capture::TAB_CAPTURE_STATE_STOPPED &&
291 request->status != tab_capture::TAB_CAPTURE_STATE_ERROR) {
292 // If we end up trying to grab a new stream while the previous one was never
293 // terminated, then something fishy is going on.
294 NOTREACHED() << "Trying to capture tab with existing stream.";
295 return;
298 if (opening_stream) {
299 request->fullscreen_observer.reset(new FullscreenObserver(request, this));
302 if (stopping_stream) {
303 request->fullscreen_observer.reset();
306 request->last_status = request->status;
307 request->status = next_state;
309 // We will get duplicate events if we requested both audio and video, so only
310 // send new events.
311 if (request->last_status != request->status) {
312 DispatchStatusChangeEvent(request);
316 void TabCaptureRegistry::DispatchStatusChangeEvent(
317 const TabCaptureRequest* request) const {
318 EventRouter* router = profile_ ? EventRouter::Get(profile_) : NULL;
319 if (!router)
320 return;
322 scoped_ptr<tab_capture::CaptureInfo> info(new tab_capture::CaptureInfo());
323 info->tab_id = request->tab_id;
324 info->status = request->status;
325 info->fullscreen = request->fullscreen;
327 scoped_ptr<base::ListValue> args(new base::ListValue());
328 args->Append(info->ToValue().release());
329 scoped_ptr<Event> event(new Event(tab_capture::OnStatusChanged::kEventName,
330 args.Pass()));
331 event->restrict_to_browser_context = profile_;
333 router->DispatchEventToExtension(request->extension_id, event.Pass());
336 TabCaptureRequest* TabCaptureRegistry::FindCaptureRequest(
337 int render_process_id, int render_view_id) const {
338 for (ScopedVector<TabCaptureRequest>::const_iterator it = requests_.begin();
339 it != requests_.end(); ++it) {
340 if ((*it)->render_process_id == render_process_id &&
341 (*it)->render_view_id == render_view_id) {
342 return *it;
345 return NULL;
348 void TabCaptureRegistry::DeleteCaptureRequest(int render_process_id,
349 int render_view_id) {
350 for (ScopedVector<TabCaptureRequest>::iterator it = requests_.begin();
351 it != requests_.end(); ++it) {
352 if ((*it)->render_process_id == render_process_id &&
353 (*it)->render_view_id == render_view_id) {
354 requests_.erase(it);
355 return;
360 } // namespace extensions