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