15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/media/media_stream_capture_indicator.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/rtl.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/memory/scoped_ptr.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/prefs/pref_service.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/app/chrome_command_ids.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/status_icons/status_icon.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/status_icons/status_tray.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/tab_contents/tab_util.h"
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/pref_names.h"
2003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "chrome/grit/chromium_strings.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/content_browser_client.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/invalidate_type.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents_delegate.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/web_contents_observer.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/theme_resources.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/image/image_skia.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(ENABLE_EXTENSIONS)
34116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chrome/common/extensions/extension_constants.h"
35116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "extensions/browser/extension_registry.h"
36116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "extensions/common/extension.h"
37116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
38116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::WebContents;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(ENABLE_EXTENSIONS)
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const extensions::Extension* GetExtension(WebContents* web_contents) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!web_contents)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  extensions::ExtensionRegistry* registry =
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      extensions::ExtensionRegistry::Get(web_contents->GetBrowserContext());
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return registry->enabled_extensions().GetExtensionOrAppByURL(
54ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      web_contents->GetURL());
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubool IsWhitelistedExtension(const extensions::Extension* extension) {
585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  static const char* const kExtensionWhitelist[] = {
615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    extension_misc::kHotwordExtensionId,
625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  };
635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  for (size_t i = 0; i < arraysize(kExtensionWhitelist); ++i) {
655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (extension->id() == kExtensionWhitelist[i])
665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return true;
675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return false;
705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
71116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif  // defined(ENABLE_EXTENSIONS)
725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Gets the security originator of the tab. It returns a string with no '/'
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// at the end to display in the UI.
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 GetSecurityOrigin(WebContents* web_contents) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!web_contents)
79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return base::string16();
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string security_origin = web_contents->GetURL().GetOrigin().spec();
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove the last character if it is a '/'.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!security_origin.empty()) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string::iterator it = security_origin.end() - 1;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (*it == '/')
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      security_origin.erase(it);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return base::UTF8ToUTF16(security_origin);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 GetTitle(WebContents* web_contents) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!web_contents)
97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return base::string16();
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
99116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(ENABLE_EXTENSIONS)
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const extensions::Extension* const extension = GetExtension(web_contents);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (extension)
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return base::UTF8ToUTF16(extension->name());
103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 tab_title = web_contents->GetTitle();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_title.empty()) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the page's title is empty use its security originator.
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_title = GetSecurityOrigin(web_contents);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the page's title matches its URL, use its security originator.
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Profile* profile =
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        Profile::FromBrowserContext(web_contents->GetBrowserContext());
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string languages =
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        profile->GetPrefs()->GetString(prefs::kAcceptLanguages);
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (tab_title == net::FormatUrl(web_contents->GetURL(), languages))
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      tab_title = GetSecurityOrigin(web_contents);
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return tab_title;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Stores usage counts for all the capture devices associated with a single
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// WebContents instance. Instances of this class are owned by
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// MediaStreamCaptureIndicator. They also observe for the destruction of the
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// WebContents instances and delete themselves when corresponding WebContents is
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// deleted.
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class MediaStreamCaptureIndicator::WebContentsDeviceUsage
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : public content::WebContentsObserver {
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  explicit WebContentsDeviceUsage(
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      scoped_refptr<MediaStreamCaptureIndicator> indicator,
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      WebContents* web_contents)
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      : WebContentsObserver(web_contents),
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        indicator_(indicator),
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        audio_ref_count_(0),
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        video_ref_count_(0),
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        mirroring_ref_count_(0),
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        weak_factory_(this) {
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool IsCapturingAudio() const { return audio_ref_count_ > 0; }
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool IsCapturingVideo() const { return video_ref_count_ > 0; }
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool IsMirroring() const { return mirroring_ref_count_ > 0; }
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<content::MediaStreamUI> RegisterMediaStream(
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      const content::MediaStreamDevices& devices);
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1517d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Increment ref-counts up based on the type of each device provided.
1523240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  void AddDevices(const content::MediaStreamDevices& devices);
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Decrement ref-counts up based on the type of each device provided.
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void RemoveDevices(const content::MediaStreamDevices& devices);
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // content::WebContentsObserver overrides.
159010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  virtual void WebContentsDestroyed() OVERRIDE {
160010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    indicator_->UnregisterWebContents(web_contents());
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    delete this;
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_refptr<MediaStreamCaptureIndicator> indicator_;
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int audio_ref_count_;
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int video_ref_count_;
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int mirroring_ref_count_;
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::WeakPtrFactory<WebContentsDeviceUsage> weak_factory_;
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(WebContentsDeviceUsage);
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Implements MediaStreamUI interface. Instances of this class are created for
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// each MediaStream and their ownership is passed to MediaStream implementation
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// in the content layer. Each UIDelegate keeps a weak pointer to the
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// corresponding WebContentsDeviceUsage object to deliver updates about state of
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// the stream.
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class MediaStreamCaptureIndicator::UIDelegate
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : public content::MediaStreamUI {
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public:
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  UIDelegate(base::WeakPtr<WebContentsDeviceUsage> device_usage,
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)             const content::MediaStreamDevices& devices)
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      : device_usage_(device_usage),
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        devices_(devices),
186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        started_(false) {
187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK(!devices_.empty());
188c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
190c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual ~UIDelegate() {
191868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (started_ && device_usage_.get())
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      device_usage_->RemoveDevices(devices_);
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) private:
196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // content::MediaStreamUI interface.
197effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  virtual gfx::NativeViewId OnStarted(const base::Closure& close_callback)
198effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      OVERRIDE {
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK(!started_);
200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    started_ = true;
201868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (device_usage_.get())
2023240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      device_usage_->AddDevices(devices_);
203effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return 0;
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::WeakPtr<WebContentsDeviceUsage> device_usage_;
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  content::MediaStreamDevices devices_;
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool started_;
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(UIDelegate);
211c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)scoped_ptr<content::MediaStreamUI>
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)MediaStreamCaptureIndicator::WebContentsDeviceUsage::RegisterMediaStream(
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const content::MediaStreamDevices& devices) {
217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return scoped_ptr<content::MediaStreamUI>(new UIDelegate(
218c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_factory_.GetWeakPtr(), devices));
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MediaStreamCaptureIndicator::WebContentsDeviceUsage::AddDevices(
2223240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    const content::MediaStreamDevices& devices) {
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (content::MediaStreamDevices::const_iterator it = devices.begin();
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != devices.end(); ++it) {
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (it->type == content::MEDIA_TAB_AUDIO_CAPTURE ||
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        it->type == content::MEDIA_TAB_VIDEO_CAPTURE) {
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ++mirroring_ref_count_;
22846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    } else if (content::IsAudioInputMediaType(it->type)) {
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ++audio_ref_count_;
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (content::IsVideoMediaType(it->type)) {
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ++video_ref_count_;
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTIMPLEMENTED();
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (web_contents())
238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
2397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
240c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  indicator_->UpdateNotificationUserInterface();
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void MediaStreamCaptureIndicator::WebContentsDeviceUsage::RemoveDevices(
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const content::MediaStreamDevices& devices) {
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (content::MediaStreamDevices::const_iterator it = devices.begin();
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != devices.end(); ++it) {
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (it->type == content::MEDIA_TAB_AUDIO_CAPTURE ||
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        it->type == content::MEDIA_TAB_VIDEO_CAPTURE) {
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      --mirroring_ref_count_;
25046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    } else if (content::IsAudioInputMediaType(it->type)) {
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      --audio_ref_count_;
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (content::IsVideoMediaType(it->type)) {
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      --video_ref_count_;
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTIMPLEMENTED();
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_GE(audio_ref_count_, 0);
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_GE(video_ref_count_, 0);
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_GE(mirroring_ref_count_, 0);
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  indicator_->UpdateNotificationUserInterface();
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MediaStreamCaptureIndicator::MediaStreamCaptureIndicator()
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : status_icon_(NULL),
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mic_image_(NULL),
2707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      camera_image_(NULL) {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MediaStreamCaptureIndicator::~MediaStreamCaptureIndicator() {
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The user is responsible for cleaning up by reporting the closure of any
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // opened devices.  However, there exists a race condition at shutdown: The UI
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // thread may be stopped before CaptureDevicesClosed() posts the task to
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // invoke DoDevicesClosedOnUIThread().  In this case, usage_map_ won't be
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // empty like it should.
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(usage_map_.empty() ||
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         !BrowserThread::IsMessageLoopValid(BrowserThread::UI));
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Free any WebContentsDeviceUsage objects left over.
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (UsageMap::const_iterator it = usage_map_.begin(); it != usage_map_.end();
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       ++it) {
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    delete it->second;
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
289c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)scoped_ptr<content::MediaStreamUI>
290c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)MediaStreamCaptureIndicator::RegisterMediaStream(
291c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* web_contents,
292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const content::MediaStreamDevices& devices) {
293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  WebContentsDeviceUsage*& usage = usage_map_[web_contents];
294c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!usage)
295c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    usage = new WebContentsDeviceUsage(this, web_contents);
296c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return usage->RegisterMediaStream(devices);
297c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void MediaStreamCaptureIndicator::ExecuteCommand(int command_id,
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 int event_flags) {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int index =
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      command_id - IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST;
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_LE(0, index);
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_GT(static_cast<int>(command_targets_.size()), index);
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  WebContents* const web_contents = command_targets_[index];
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UsageMap::const_iterator it = usage_map_.find(web_contents);
309c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (it == usage_map_.end())
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  web_contents->GetDelegate()->ActivateContents(web_contents);
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool MediaStreamCaptureIndicator::IsCapturingUserMedia(
315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* web_contents) const {
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UsageMap::const_iterator it = usage_map_.find(web_contents);
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (it != usage_map_.end() &&
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (it->second->IsCapturingAudio() || it->second->IsCapturingVideo()));
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)bool MediaStreamCaptureIndicator::IsCapturingVideo(
3247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    content::WebContents* web_contents) const {
3257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
3277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  UsageMap::const_iterator it = usage_map_.find(web_contents);
3287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return (it != usage_map_.end() && it->second->IsCapturingVideo());
3297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
3307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
3317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)bool MediaStreamCaptureIndicator::IsCapturingAudio(
3327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    content::WebContents* web_contents) const {
3337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
3357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  UsageMap::const_iterator it = usage_map_.find(web_contents);
3367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return (it != usage_map_.end() && it->second->IsCapturingAudio());
3377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
3387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool MediaStreamCaptureIndicator::IsBeingMirrored(
340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* web_contents) const {
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UsageMap::const_iterator it = usage_map_.find(web_contents);
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return it != usage_map_.end() && it->second->IsMirroring();
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
347c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MediaStreamCaptureIndicator::UnregisterWebContents(
348c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    WebContents* web_contents) {
349c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  usage_map_.erase(web_contents);
350c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  UpdateNotificationUserInterface();
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
353a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void MediaStreamCaptureIndicator::MaybeCreateStatusTrayIcon(bool audio,
354a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                                            bool video) {
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status_icon_)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If there is no browser process, we should not create the status tray.
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!g_browser_process)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusTray* status_tray = g_browser_process->status_tray();
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_tray)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnsureStatusTrayIconResources();
368a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
369a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  gfx::ImageSkia image;
370a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 tool_tip;
371a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  GetStatusTrayIconInfo(audio, video, &image, &tool_tip);
372a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK(!image.isNull());
373a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK(!tool_tip.empty());
374a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
375a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  status_icon_ = status_tray->CreateStatusIcon(
376a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      StatusTray::MEDIA_STREAM_CAPTURE_ICON, image, tool_tip);
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MediaStreamCaptureIndicator::EnsureStatusTrayIconResources() {
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!mic_image_) {
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mic_image_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        IDR_INFOBAR_MEDIA_STREAM_MIC);
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!camera_image_) {
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    camera_image_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        IDR_INFOBAR_MEDIA_STREAM_CAMERA);
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(mic_image_);
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(camera_image_);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void MediaStreamCaptureIndicator::MaybeDestroyStatusTrayIcon() {
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_icon_)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If there is no browser process, we should not do anything.
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!g_browser_process)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusTray* status_tray = g_browser_process->status_tray();
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status_tray != NULL) {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    status_tray->RemoveStatusIcon(status_icon_);
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    status_icon_ = NULL;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void MediaStreamCaptureIndicator::UpdateNotificationUserInterface() {
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
412424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool audio = false;
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool video = false;
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int command_id = IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_FIRST;
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  command_targets_.clear();
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (UsageMap::const_iterator iter = usage_map_.begin();
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter != usage_map_.end(); ++iter) {
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Check if any audio and video devices have been used.
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const WebContentsDeviceUsage& usage = *iter->second;
4235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (!usage.IsCapturingAudio() && !usage.IsCapturingVideo())
4245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      continue;
4255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    WebContents* const web_contents = iter->first;
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // The audio/video icon is shown only for non-whitelisted extensions or on
4295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Android. For regular tabs on desktop, we show an indicator in the tab
4305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // icon.
431116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(ENABLE_EXTENSIONS)
4325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    const extensions::Extension* extension = GetExtension(web_contents);
4335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (!extension || IsWhitelistedExtension(extension))
4345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      continue;
435c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#endif
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    audio = audio || usage.IsCapturingAudio();
4385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    video = video || usage.IsCapturingVideo();
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    command_targets_.push_back(web_contents);
4415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    menu->AddItem(command_id, GetTitle(web_contents));
442424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
4435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // If the menu item is not a label, enable it.
4445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    menu->SetCommandIdEnabled(command_id,
4455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                              command_id != IDC_MinimumLabelValue);
4465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // If reaching the maximum number, no more item will be added to the menu.
4485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (command_id == IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_LAST)
4495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
4505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    ++command_id;
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (command_targets_.empty()) {
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    MaybeDestroyStatusTrayIcon();
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The icon will take the ownership of the passed context menu.
459a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  MaybeCreateStatusTrayIcon(audio, video);
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (status_icon_) {
461424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    status_icon_->SetContextMenu(menu.Pass());
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MediaStreamCaptureIndicator::GetStatusTrayIconInfo(
466a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    bool audio,
467a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    bool video,
468a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    gfx::ImageSkia* image,
469a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16* tool_tip) {
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(audio || video);
472a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int message_id = 0;
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (audio && video) {
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO;
476a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *image = *camera_image_;
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (audio && !video) {
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY;
479a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *image = *mic_image_;
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (!audio && video) {
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY;
482a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *image = *camera_image_;
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  *tool_tip = l10n_util::GetStringUTF16(message_id);
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
487