tab_capture_registry.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 "content/public/browser/browser_thread.h" 8#include "chrome/browser/extensions/event_names.h" 9#include "chrome/browser/extensions/event_router.h" 10#include "chrome/browser/extensions/extension_system.h" 11#include "chrome/browser/media/media_internals.h" 12#include "chrome/browser/profiles/profile.h" 13#include "chrome/browser/profiles/profile_dependency_manager.h" 14#include "chrome/common/chrome_notification_types.h" 15#include "chrome/common/extensions/extension.h" 16#include "content/public/browser/notification_details.h" 17#include "content/public/browser/notification_source.h" 18 19namespace events = extensions::event_names; 20using content::BrowserThread; 21 22namespace extensions { 23 24TabCaptureRegistry::TabCaptureRegistry(Profile* profile) 25 : proxy_(new MediaObserverProxy()), profile_(profile) { 26 proxy_->Attach(this); 27 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 28 content::Source<Profile>(profile_)); 29} 30 31TabCaptureRegistry::~TabCaptureRegistry() { 32 proxy_->Detach(); 33} 34 35void TabCaptureRegistry::HandleRequestUpdateOnUIThread( 36 const content::MediaStreamDevice& device, 37 const content::MediaRequestState new_state) { 38 EventRouter* router = profile_ ? 39 extensions::ExtensionSystem::Get(profile_)->event_router() : NULL; 40 if (!router) 41 return; 42 43 if (requests_.find(device.device_id) == requests_.end()) 44 return; 45 46 tab_capture::TabCaptureState state = 47 tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_NONE; 48 switch (new_state) { 49 case content::MEDIA_REQUEST_STATE_REQUESTED: 50 state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_REQUESTED; 51 break; 52 case content::MEDIA_REQUEST_STATE_PENDING_APPROVAL: 53 state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_PENDING; 54 break; 55 case content::MEDIA_REQUEST_STATE_DONE: 56 state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_ACTIVE; 57 break; 58 case content::MEDIA_REQUEST_STATE_CLOSING: 59 state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_STOPPED; 60 break; 61 case content::MEDIA_REQUEST_STATE_ERROR: 62 state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_ERROR; 63 break; 64 default: 65 // TODO(justinlin): Implement muted state notification. 66 break; 67 } 68 69 if (state == tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_NONE) { 70 // This is a state we don't handle. 71 return; 72 } 73 74 TabCaptureRegistry::TabCaptureRequest& request_info = 75 requests_[device.device_id]; 76 request_info.status = state; 77 78 scoped_ptr<tab_capture::CaptureInfo> info(new tab_capture::CaptureInfo()); 79 info->tab_id = request_info.tab_id; 80 info->status = request_info.status; 81 82 scoped_ptr<base::ListValue> args(new ListValue()); 83 args->Append(info->ToValue().release()); 84 router->DispatchEventToExtension(request_info.extension_id, 85 events::kOnTabCaptureStatusChanged, args.Pass(), profile_, GURL()); 86} 87 88const TabCaptureRegistry::CaptureRequestList 89 TabCaptureRegistry::GetCapturedTabs(const std::string& extension_id) { 90 CaptureRequestList list; 91 for (DeviceCaptureRequestMap::iterator it = requests_.begin(); 92 it != requests_.end(); ++it) { 93 if (it->second.extension_id == extension_id) { 94 list.push_back(it->second); 95 } 96 } 97 return list; 98} 99 100void TabCaptureRegistry::Observe(int type, 101 const content::NotificationSource& source, 102 const content::NotificationDetails& details) { 103 switch (type) { 104 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { 105 // Cleanup all the requested media streams for this extension. We might 106 // accumulate too many requests left in the closed state otherwise. 107 std::string extension_id = 108 content::Details<extensions::UnloadedExtensionInfo>(details)-> 109 extension->id(); 110 for (DeviceCaptureRequestMap::iterator it = requests_.begin(); 111 it != requests_.end();) { 112 if (it->second.extension_id == extension_id) { 113 requests_.erase(it++); 114 } else { 115 ++it; 116 } 117 } 118 break; 119 } 120 } 121} 122 123bool TabCaptureRegistry::AddRequest( 124 const std::string& key, const TabCaptureRequest& request) { 125 // Currently, we do not allow multiple active captures for same tab. 126 DCHECK(!key.empty()); 127 if (requests_.find(key) != requests_.end()) 128 if (requests_[key].status != 129 tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_STOPPED && 130 requests_[key].status != 131 tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_ERROR) 132 return false; 133 requests_[key] = request; 134 return true; 135} 136 137bool TabCaptureRegistry::VerifyRequest(const std::string& key) { 138 return requests_.find(key) != requests_.end(); 139} 140 141void TabCaptureRegistry::MediaObserverProxy::Attach( 142 TabCaptureRegistry* request_handler) { 143 handler_ = request_handler; 144 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 145 base::Bind(&TabCaptureRegistry::MediaObserverProxy:: 146 RegisterAsMediaObserverOnIOThread, this, false)); 147} 148 149void TabCaptureRegistry::MediaObserverProxy::Detach() { 150 handler_ = NULL; 151 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 152 base::Bind(&TabCaptureRegistry::MediaObserverProxy:: 153 RegisterAsMediaObserverOnIOThread, this, true)); 154} 155 156void TabCaptureRegistry::MediaObserverProxy::RegisterAsMediaObserverOnIOThread( 157 bool unregister) { 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 159 if (MediaInternals::GetInstance()) { 160 if (!unregister) 161 MediaInternals::GetInstance()->AddObserver(this); 162 else 163 MediaInternals::GetInstance()->RemoveObserver(this); 164 } 165} 166 167void TabCaptureRegistry::MediaObserverProxy::OnRequestUpdate( 168 const content::MediaStreamDevice& device, 169 const content::MediaRequestState new_state) { 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 171 172 // TODO(justinlin): We drop audio device events since they will occur in 173 // parallel with the video device events (we would get duplicate events). When 174 // audio mirroring is implemented, we will want to grab those events when 175 // video is not requested. 176 if (device.type != content::MEDIA_TAB_VIDEO_CAPTURE) 177 return; 178 179 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 180 base::Bind(&TabCaptureRegistry::MediaObserverProxy::UpdateOnUIThread, 181 this, device, new_state)); 182} 183 184void TabCaptureRegistry::MediaObserverProxy::UpdateOnUIThread( 185 const content::MediaStreamDevice& device, 186 const content::MediaRequestState new_state) { 187 if (handler_) 188 handler_->HandleRequestUpdateOnUIThread(device, new_state); 189} 190 191} // namespace extensions 192