| Index: chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
 | 
| diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..b74c2d547b68e193b33e3e98422c94a2b7c28f56
 | 
| --- /dev/null
 | 
| +++ b/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
 | 
| @@ -0,0 +1,189 @@
 | 
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
 | 
| +
 | 
| +#include "content/public/browser/browser_thread.h"
 | 
| +#include "chrome/browser/extensions/event_names.h"
 | 
| +#include "chrome/browser/extensions/event_router.h"
 | 
| +#include "chrome/browser/media/media_internals.h"
 | 
| +#include "chrome/browser/profiles/profile.h"
 | 
| +#include "chrome/browser/profiles/profile_dependency_manager.h"
 | 
| +#include "chrome/common/chrome_notification_types.h"
 | 
| +#include "chrome/common/extensions/extension.h"
 | 
| +#include "content/public/browser/notification_details.h"
 | 
| +#include "content/public/browser/notification_source.h"
 | 
| +
 | 
| +namespace events = extensions::event_names;
 | 
| +using content::BrowserThread;
 | 
| +
 | 
| +namespace extensions {
 | 
| +
 | 
| +TabCaptureRegistry::TabCaptureRegistry(Profile* profile)
 | 
| +    : proxy_(new MediaObserverProxy()), profile_(profile) {
 | 
| +  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| +  proxy_->Attach(this);
 | 
| +  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
 | 
| +                 content::Source<Profile>(profile_));
 | 
| +}
 | 
| +
 | 
| +TabCaptureRegistry::~TabCaptureRegistry() {
 | 
| +  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| +  proxy_->Detach();
 | 
| +}
 | 
| +
 | 
| +void TabCaptureRegistry::HandleRequestUpdateOnUIThread(
 | 
| +    const content::MediaStreamDevice& device,
 | 
| +    const content::MediaRequestState new_state) {
 | 
| +  EventRouter* router = profile_ ? profile_->GetExtensionEventRouter() : NULL;
 | 
| +  if (!router)
 | 
| +    return;
 | 
| +
 | 
| +  if (requests_.find(device.device_id) == requests_.end())
 | 
| +    return;
 | 
| +
 | 
| +  tab_capture::TabCaptureState state =
 | 
| +      tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_NONE;
 | 
| +  switch (new_state) {
 | 
| +    case content::MEDIA_REQUEST_STATE_REQUESTED:
 | 
| +      state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_REQUESTED;
 | 
| +      break;
 | 
| +    case content::MEDIA_REQUEST_STATE_PENDING_APPROVAL:
 | 
| +      state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_PENDING;
 | 
| +      break;
 | 
| +    case content::MEDIA_REQUEST_STATE_DONE:
 | 
| +      state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_ACTIVE;
 | 
| +      break;
 | 
| +    case content::MEDIA_REQUEST_STATE_CLOSING:
 | 
| +      state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_STOPPED;
 | 
| +      break;
 | 
| +    case content::MEDIA_REQUEST_STATE_ERROR:
 | 
| +      state = tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_ERROR;
 | 
| +      break;
 | 
| +    default:
 | 
| +      // TODO(justinlin): Implement muted state notification.
 | 
| +      break;
 | 
| +  }
 | 
| +
 | 
| +  if (state == tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_NONE) {
 | 
| +    // This is a state we don't handle.
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  TabCaptureRegistry::TabCaptureRequest& request_info =
 | 
| +      requests_[device.device_id];
 | 
| +  request_info.status = state;
 | 
| +
 | 
| +  scoped_ptr<tab_capture::CaptureInfo> info(new tab_capture::CaptureInfo());
 | 
| +  info->tab_id = request_info.tab_id;
 | 
| +  info->status = request_info.status;
 | 
| +
 | 
| +  scoped_ptr<base::ListValue> args(new ListValue());
 | 
| +  args->Append(info->ToValue().release());
 | 
| +  router->DispatchEventToExtension(request_info.extension_id,
 | 
| +      events::kOnTabCaptureStatusChanged, args.Pass(), profile_, GURL());
 | 
| +}
 | 
| +
 | 
| +const TabCaptureRegistry::CaptureRequestList
 | 
| +    TabCaptureRegistry::GetCapturedTabs(const std::string& extension_id) {
 | 
| +  CaptureRequestList list;
 | 
| +  for (DeviceCaptureRequestMap::iterator it = requests_.begin();
 | 
| +       it != requests_.end(); ++it) {
 | 
| +    if (it->second.extension_id == extension_id) {
 | 
| +      list.push_back(it->second);
 | 
| +    }
 | 
| +  }
 | 
| +  return list;
 | 
| +}
 | 
| +
 | 
| +void TabCaptureRegistry::Observe(int type,
 | 
| +                                 const content::NotificationSource& source,
 | 
| +                                 const content::NotificationDetails& details) {
 | 
| +  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| +  switch (type) {
 | 
| +    case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
 | 
| +      // Cleanup all the requested media streams for this extension. We might
 | 
| +      // accumulate too many requests left in the closed state otherwise.
 | 
| +      std::string extension_id =
 | 
| +          content::Details<extensions::UnloadedExtensionInfo>(details)->
 | 
| +              extension->id();
 | 
| +      for (DeviceCaptureRequestMap::iterator it = requests_.begin();
 | 
| +           it != requests_.end();) {
 | 
| +        if (it->second.extension_id == extension_id) {
 | 
| +          requests_.erase(it++);
 | 
| +        } else {
 | 
| +          ++it;
 | 
| +        }
 | 
| +      }
 | 
| +      break;
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +bool TabCaptureRegistry::AddRequest(
 | 
| +    const std::string& key, const TabCaptureRequest& request) {
 | 
| +  // Currently, we do not allow multiple active captures for same tab.
 | 
| +  DCHECK(!key.empty());
 | 
| +  if (requests_.find(key) != requests_.end())
 | 
| +    if (requests_[key].status !=
 | 
| +        tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_STOPPED &&
 | 
| +        requests_[key].status !=
 | 
| +        tab_capture::TAB_CAPTURE_TAB_CAPTURE_STATE_ERROR)
 | 
| +      return false;
 | 
| +  requests_[key] = request;
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +void TabCaptureRegistry::MediaObserverProxy::Attach(
 | 
| +    TabCaptureRegistry* request_handler) {
 | 
| +  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| +  handler_ = request_handler;
 | 
| +  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
 | 
| +      base::Bind(&TabCaptureRegistry::MediaObserverProxy::
 | 
| +                 RegisterAsMediaObserverOnIOThread, this, false));
 | 
| +}
 | 
| +
 | 
| +void TabCaptureRegistry::MediaObserverProxy::Detach() {
 | 
| +  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
| +  handler_ = NULL;
 | 
| +  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
 | 
| +      base::Bind(&TabCaptureRegistry::MediaObserverProxy::
 | 
| +                 RegisterAsMediaObserverOnIOThread, this, true));
 | 
| +}
 | 
| +
 | 
| +void TabCaptureRegistry::MediaObserverProxy::RegisterAsMediaObserverOnIOThread(
 | 
| +      bool unregister) {
 | 
| +  if (MediaInternals::GetInstance()) {
 | 
| +    if (!unregister)
 | 
| +      MediaInternals::GetInstance()->AddObserver(this);
 | 
| +    else
 | 
| +      MediaInternals::GetInstance()->RemoveObserver(this);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void TabCaptureRegistry::MediaObserverProxy::OnRequestUpdate(
 | 
| +    const content::MediaStreamDevice& device,
 | 
| +    const content::MediaRequestState new_state) {
 | 
| +  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
 | 
| +
 | 
| +  // TODO(justinlin): We drop audio device events since they will occur in
 | 
| +  // parallel with the video device events (we would get duplicate events). When
 | 
| +  // audio mirroring is implemented, we will want to grab those events when
 | 
| +  // video is not requested.
 | 
| +  if (device.type != content::MEDIA_TAB_VIDEO_CAPTURE)
 | 
| +    return;
 | 
| +
 | 
| +  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
 | 
| +      base::Bind(&TabCaptureRegistry::MediaObserverProxy::UpdateOnUIThread,
 | 
| +          this, device, new_state));
 | 
| +}
 | 
| +
 | 
| +void TabCaptureRegistry::MediaObserverProxy::UpdateOnUIThread(
 | 
| +    const content::MediaStreamDevice& device,
 | 
| +    const content::MediaRequestState new_state) {
 | 
| +  if (handler_)
 | 
| +    handler_->HandleRequestUpdateOnUIThread(device, new_state);
 | 
| +}
 | 
| +
 | 
| +}  // namespace extensions
 | 
| 
 |