Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / tab_capture / tab_capture_registry.cc
blob866f6ceae240c9ceed18353d4c75964b655f1b5d
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 "base/values.h"
9 #include "chrome/browser/sessions/session_tab_helper.h"
10 #include "components/keyed_service/content/browser_context_dependency_manager.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/render_frame_host.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/browser/web_contents_observer.h"
15 #include "extensions/browser/event_router.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/common/extension.h"
19 using content::BrowserThread;
20 using extensions::tab_capture::TabCaptureState;
22 namespace extensions {
24 namespace tab_capture = api::tab_capture;
26 // Stores values associated with a tab capture request, maintains lifecycle
27 // state, and monitors WebContents for fullscreen transition events and
28 // destruction.
29 class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver {
30 public:
31 LiveRequest(content::WebContents* target_contents,
32 const std::string& extension_id,
33 TabCaptureRegistry* registry)
34 : content::WebContentsObserver(target_contents),
35 extension_id_(extension_id),
36 registry_(registry),
37 capture_state_(tab_capture::TAB_CAPTURE_STATE_NONE),
38 is_verified_(false),
39 // TODO(miu): This initial value for |is_fullscreened_| is a faulty
40 // assumption. http://crbug.com/350491
41 is_fullscreened_(false),
42 render_process_id_(-1),
43 render_frame_id_(-1) {
44 DCHECK(web_contents());
45 DCHECK(registry_);
48 ~LiveRequest() override {}
50 // Accessors.
51 const std::string& extension_id() const {
52 return extension_id_;
54 TabCaptureState capture_state() const {
55 return capture_state_;
57 bool is_verified() const {
58 return is_verified_;
61 void SetIsVerified() {
62 DCHECK(!is_verified_);
63 is_verified_ = true;
66 // TODO(miu): See TODO(miu) in VerifyRequest() below.
67 void SetOriginallyTargettedRenderFrameID(int render_process_id,
68 int render_frame_id) {
69 DCHECK_GT(render_frame_id, 0);
70 DCHECK_EQ(render_frame_id_, -1); // Setting ID only once.
71 render_process_id_ = render_process_id;
72 render_frame_id_ = render_frame_id;
75 bool WasOriginallyTargettingRenderFrameID(int render_process_id,
76 int render_frame_id) const {
77 return render_process_id_ == render_process_id &&
78 render_frame_id_ == render_frame_id;
81 void UpdateCaptureState(TabCaptureState next_capture_state) {
82 // This method can get duplicate calls if both audio and video were
83 // requested, so return early to avoid duplicate dispatching of status
84 // change events.
85 if (capture_state_ == next_capture_state)
86 return;
88 capture_state_ = next_capture_state;
89 registry_->DispatchStatusChangeEvent(this);
92 void GetCaptureInfo(tab_capture::CaptureInfo* info) const {
93 info->tab_id = SessionTabHelper::IdForTab(web_contents());
94 info->status = capture_state_;
95 info->fullscreen = is_fullscreened_;
98 protected:
99 void DidShowFullscreenWidget(int routing_id) override {
100 is_fullscreened_ = true;
101 if (capture_state_ == tab_capture::TAB_CAPTURE_STATE_ACTIVE)
102 registry_->DispatchStatusChangeEvent(this);
105 void DidDestroyFullscreenWidget(int routing_id) override {
106 is_fullscreened_ = false;
107 if (capture_state_ == tab_capture::TAB_CAPTURE_STATE_ACTIVE)
108 registry_->DispatchStatusChangeEvent(this);
111 void DidToggleFullscreenModeForTab(bool entered_fullscreen) override {
112 is_fullscreened_ = entered_fullscreen;
113 if (capture_state_ == tab_capture::TAB_CAPTURE_STATE_ACTIVE)
114 registry_->DispatchStatusChangeEvent(this);
117 void WebContentsDestroyed() override {
118 registry_->KillRequest(this); // Deletes |this|.
121 private:
122 const std::string extension_id_;
123 TabCaptureRegistry* const registry_;
124 TabCaptureState capture_state_;
125 bool is_verified_;
126 bool is_fullscreened_;
128 // These reference the originally targetted RenderFrameHost by its ID. The
129 // RenderFrameHost may have gone away long before a LiveRequest closes, but
130 // calls to OnRequestUpdate() will always refer to this request by this ID.
131 int render_process_id_;
132 int render_frame_id_;
134 DISALLOW_COPY_AND_ASSIGN(LiveRequest);
137 TabCaptureRegistry::TabCaptureRegistry(content::BrowserContext* context)
138 : browser_context_(context), extension_registry_observer_(this) {
139 MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this);
140 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
143 TabCaptureRegistry::~TabCaptureRegistry() {
144 MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this);
147 // static
148 TabCaptureRegistry* TabCaptureRegistry::Get(content::BrowserContext* context) {
149 return BrowserContextKeyedAPIFactory<TabCaptureRegistry>::Get(context);
152 static base::LazyInstance<BrowserContextKeyedAPIFactory<TabCaptureRegistry> >
153 g_factory = LAZY_INSTANCE_INITIALIZER;
155 // static
156 BrowserContextKeyedAPIFactory<TabCaptureRegistry>*
157 TabCaptureRegistry::GetFactoryInstance() {
158 return g_factory.Pointer();
161 void TabCaptureRegistry::GetCapturedTabs(
162 const std::string& extension_id,
163 base::ListValue* list_of_capture_info) const {
164 DCHECK_CURRENTLY_ON(BrowserThread::UI);
165 DCHECK(list_of_capture_info);
166 list_of_capture_info->Clear();
167 for (ScopedVector<LiveRequest>::const_iterator it = requests_.begin();
168 it != requests_.end(); ++it) {
169 if ((*it)->is_verified() && (*it)->extension_id() == extension_id) {
170 tab_capture::CaptureInfo info;
171 (*it)->GetCaptureInfo(&info);
172 list_of_capture_info->Append(info.ToValue().release());
177 void TabCaptureRegistry::OnExtensionUnloaded(
178 content::BrowserContext* browser_context,
179 const Extension* extension,
180 UnloadedExtensionInfo::Reason reason) {
181 // Cleanup all the requested media streams for this extension.
182 for (ScopedVector<LiveRequest>::iterator it = requests_.begin();
183 it != requests_.end();) {
184 if ((*it)->extension_id() == extension->id()) {
185 it = requests_.erase(it);
186 } else {
187 ++it;
192 bool TabCaptureRegistry::AddRequest(content::WebContents* target_contents,
193 const std::string& extension_id) {
194 LiveRequest* const request = FindRequest(target_contents);
196 // Currently, we do not allow multiple active captures for same tab.
197 if (request != NULL) {
198 if (request->capture_state() != tab_capture::TAB_CAPTURE_STATE_STOPPED &&
199 request->capture_state() != tab_capture::TAB_CAPTURE_STATE_ERROR) {
200 return false;
201 } else {
202 // Delete the request before creating its replacement (below).
203 KillRequest(request);
207 requests_.push_back(new LiveRequest(target_contents, extension_id, this));
208 return true;
211 bool TabCaptureRegistry::VerifyRequest(
212 int render_process_id,
213 int render_frame_id,
214 const std::string& extension_id) {
215 DCHECK_CURRENTLY_ON(BrowserThread::UI);
217 LiveRequest* const request = FindRequest(
218 content::WebContents::FromRenderFrameHost(
219 content::RenderFrameHost::FromID(
220 render_process_id, render_frame_id)));
221 if (!request)
222 return false; // Unknown RenderFrameHost ID, or frame has gone away.
224 // TODO(miu): We should probably also verify the origin URL, like the desktop
225 // capture API. http://crbug.com/163100
226 if (request->is_verified() ||
227 request->extension_id() != extension_id ||
228 (request->capture_state() != tab_capture::TAB_CAPTURE_STATE_NONE &&
229 request->capture_state() != tab_capture::TAB_CAPTURE_STATE_PENDING))
230 return false;
232 // TODO(miu): The RenderFrameHost IDs should be set when LiveRequest is
233 // constructed, but ExtensionFunction does not yet support use of
234 // render_frame_host() to determine the exact RenderFrameHost for the call to
235 // AddRequest() above. Fix tab_capture_api.cc, and then fix this ugly hack.
236 // http://crbug.com/304341
237 request->SetOriginallyTargettedRenderFrameID(
238 render_process_id, render_frame_id);
240 request->SetIsVerified();
241 return true;
244 void TabCaptureRegistry::OnRequestUpdate(
245 int original_target_render_process_id,
246 int original_target_render_frame_id,
247 content::MediaStreamType stream_type,
248 const content::MediaRequestState new_state) {
249 DCHECK_CURRENTLY_ON(BrowserThread::UI);
250 if (stream_type != content::MEDIA_TAB_VIDEO_CAPTURE &&
251 stream_type != content::MEDIA_TAB_AUDIO_CAPTURE) {
252 return;
255 LiveRequest* request = FindRequest(original_target_render_process_id,
256 original_target_render_frame_id);
257 if (!request) {
258 // Fall-back: Search again using WebContents since this method may have been
259 // called before VerifyRequest() set the RenderFrameHost ID. If the
260 // RenderFrameHost has gone away, that's okay since the upcoming call to
261 // VerifyRequest() will fail, and that means the tracking of request updates
262 // doesn't matter anymore.
263 request = FindRequest(content::WebContents::FromRenderFrameHost(
264 content::RenderFrameHost::FromID(original_target_render_process_id,
265 original_target_render_frame_id)));
266 if (!request)
267 return; // Stale or invalid request update.
270 TabCaptureState next_state = tab_capture::TAB_CAPTURE_STATE_NONE;
271 switch (new_state) {
272 case content::MEDIA_REQUEST_STATE_PENDING_APPROVAL:
273 next_state = tab_capture::TAB_CAPTURE_STATE_PENDING;
274 break;
275 case content::MEDIA_REQUEST_STATE_DONE:
276 next_state = tab_capture::TAB_CAPTURE_STATE_ACTIVE;
277 break;
278 case content::MEDIA_REQUEST_STATE_CLOSING:
279 next_state = tab_capture::TAB_CAPTURE_STATE_STOPPED;
280 break;
281 case content::MEDIA_REQUEST_STATE_ERROR:
282 next_state = tab_capture::TAB_CAPTURE_STATE_ERROR;
283 break;
284 case content::MEDIA_REQUEST_STATE_OPENING:
285 return;
286 case content::MEDIA_REQUEST_STATE_REQUESTED:
287 case content::MEDIA_REQUEST_STATE_NOT_REQUESTED:
288 NOTREACHED();
289 return;
292 if (next_state == tab_capture::TAB_CAPTURE_STATE_PENDING &&
293 request->capture_state() != tab_capture::TAB_CAPTURE_STATE_PENDING &&
294 request->capture_state() != tab_capture::TAB_CAPTURE_STATE_NONE &&
295 request->capture_state() != tab_capture::TAB_CAPTURE_STATE_STOPPED &&
296 request->capture_state() != tab_capture::TAB_CAPTURE_STATE_ERROR) {
297 // If we end up trying to grab a new stream while the previous one was never
298 // terminated, then something fishy is going on.
299 NOTREACHED() << "Trying to capture tab with existing stream.";
300 return;
303 request->UpdateCaptureState(next_state);
306 void TabCaptureRegistry::DispatchStatusChangeEvent(
307 const LiveRequest* request) const {
308 EventRouter* router = EventRouter::Get(browser_context_);
309 if (!router)
310 return;
312 scoped_ptr<base::ListValue> args(new base::ListValue());
313 tab_capture::CaptureInfo info;
314 request->GetCaptureInfo(&info);
315 args->Append(info.ToValue().release());
316 scoped_ptr<Event> event(new Event(events::TAB_CAPTURE_ON_STATUS_CHANGED,
317 tab_capture::OnStatusChanged::kEventName,
318 args.Pass()));
319 event->restrict_to_browser_context = browser_context_;
321 router->DispatchEventToExtension(request->extension_id(), event.Pass());
324 TabCaptureRegistry::LiveRequest* TabCaptureRegistry::FindRequest(
325 const content::WebContents* target_contents) const {
326 for (ScopedVector<LiveRequest>::const_iterator it = requests_.begin();
327 it != requests_.end(); ++it) {
328 if ((*it)->web_contents() == target_contents)
329 return *it;
331 return NULL;
334 TabCaptureRegistry::LiveRequest* TabCaptureRegistry::FindRequest(
335 int original_target_render_process_id,
336 int original_target_render_frame_id) const {
337 for (ScopedVector<LiveRequest>::const_iterator it = requests_.begin();
338 it != requests_.end(); ++it) {
339 if ((*it)->WasOriginallyTargettingRenderFrameID(
340 original_target_render_process_id,
341 original_target_render_frame_id))
342 return *it;
344 return NULL;
347 void TabCaptureRegistry::KillRequest(LiveRequest* request) {
348 for (ScopedVector<LiveRequest>::iterator it = requests_.begin();
349 it != requests_.end(); ++it) {
350 if ((*it) == request) {
351 requests_.erase(it);
352 return;
355 NOTREACHED();
358 } // namespace extensions