Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media / media_capture_devices_dispatcher.cc
index 93d462b..30d3d75 100644 (file)
@@ -4,26 +4,31 @@
 
 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
 
+#include "apps/app_window.h"
+#include "apps/app_window_registry.h"
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/metrics/field_trial.h"
 #include "base/prefs/pref_service.h"
 #include "base/prefs/scoped_user_pref_update.h"
 #include "base/sha1.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
 #include "chrome/browser/media/desktop_streams_registry.h"
 #include "chrome/browser/media/media_stream_capture_indicator.h"
 #include "chrome/browser/media/media_stream_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/screen_capture_notification_ui.h"
 #include "chrome/browser/ui/simple_message_box.h"
 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_version_info.h"
 #include "chrome/common/pref_names.h"
-#include "components/user_prefs/pref_registry_syncable.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/desktop_media_id.h"
 #include "content/public/browser/media_capture_devices.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/media_stream_request.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/permissions/permissions_data.h"
 #include "grit/generated_resources.h"
 #include "media/audio/audio_manager_base.h"
 #include "media/base/media_switches.h"
 #include "chrome/browser/media/audio_stream_monitor.h"
 #endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
+#if defined(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
+#endif
+
 using content::BrowserThread;
 using content::MediaCaptureDevices;
 using content::MediaStreamDevices;
 
 namespace {
 
+// A finch experiment to enable the permission bubble for media requests only.
+bool MediaStreamPermissionBubbleExperimentEnabled() {
+  const std::string group =
+      base::FieldTrialList::FindFullName("MediaStreamPermissionBubble");
+  if (group == "enabled")
+    return true;
+
+  return false;
+}
+
 // Finds a device in |devices| that has |device_id|, or NULL if not found.
 const content::MediaStreamDevice* FindDeviceWithId(
     const content::MediaStreamDevices& devices,
@@ -70,7 +91,7 @@ const content::MediaStreamDevice* FindDeviceWithId(
     }
   }
   return NULL;
-};
+}
 
 // This is a short-term solution to grant camera and/or microphone access to
 // extensions:
@@ -89,60 +110,27 @@ bool IsMediaRequestWhitelistedForExtension(
       extension->id() == "nnckehldicaciogcbchegobnafnjkcne";
 }
 
-// This is a short-term solution to allow testing of the the Screen Capture API
-// with Google Hangouts in M27.
-// TODO(sergeyu): Remove this whitelist as soon as possible.
-bool IsOriginWhitelistedForScreenCapture(const GURL& origin) {
-#if defined(OFFICIAL_BUILD)
-  if (// Google Hangouts.
-      (origin.SchemeIs("https") &&
-       EndsWith(origin.spec(), ".talkgadget.google.com/", true)) ||
-      origin.spec() == "https://talkgadget.google.com/" ||
-      origin.spec() == "https://plus.google.com/" ||
-      origin.spec() == "chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/" ||
-      origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
-      origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
-      origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/" ||
-      origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/") {
-    return true;
-  }
-  // Check against hashed origins.
-  // TODO(hshi): remove this when trusted tester becomes public.
-  const std::string origin_hash = base::SHA1HashString(origin.spec());
-  DCHECK_EQ(origin_hash.length(), base::kSHA1Length);
-  const std::string hexencoded_origin_hash =
-      base::HexEncode(origin_hash.data(), origin_hash.length());
+bool IsBuiltInExtension(const GURL& origin) {
   return
-      hexencoded_origin_hash == "3C2705BC432E7C51CA8553FDC5BEE873FF2468EE";
-#else
-  return false;
-#endif
+      // Feedback Extension.
+      origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/";
 }
 
-#if defined(OS_CHROMEOS)
 // Returns true of the security origin is associated with casting.
 bool IsOriginForCasting(const GURL& origin) {
-#if defined(OFFICIAL_BUILD)
   // Whitelisted tab casting extensions.
-  if (origin.spec() == "chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/" ||
-      origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
-      origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
-      origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/") {
-    return true;
-  }
-  // Check against hashed origins.
-  // TODO(hshi): remove this when trusted tester becomes public.
-  const std::string origin_hash = base::SHA1HashString(origin.spec());
-  DCHECK_EQ(origin_hash.length(), base::kSHA1Length);
-  const std::string hexencoded_origin_hash =
-      base::HexEncode(origin_hash.data(), origin_hash.length());
   return
-      hexencoded_origin_hash == "3C2705BC432E7C51CA8553FDC5BEE873FF2468EE";
-#else
-  return false;
-#endif
+      // Dev
+      origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" ||
+      // Canary
+      origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
+      // Beta (internal)
+      origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
+      // Google Cast Beta
+      origin.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" ||
+      // Google Cast Stable
+      origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/";
 }
-#endif
 
 // Helper to get title of the calling application shown in the screen capture
 // notification.
@@ -238,6 +226,28 @@ void StopAudioStreamMonitoringOnUIThread(
 
 #endif  // defined(AUDIO_STREAM_MONITORING)
 
+#if !defined(OS_ANDROID)
+// Find browser or app window from a given |web_contents|.
+gfx::NativeWindow FindParentWindowForWebContents(
+    content::WebContents* web_contents) {
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
+  if (browser && browser->window())
+    return browser->window()->GetNativeWindow();
+
+  const apps::AppWindowRegistry::AppWindowList& window_list =
+      apps::AppWindowRegistry::Get(
+          web_contents->GetBrowserContext())->app_windows();
+  for (apps::AppWindowRegistry::AppWindowList::const_iterator iter =
+           window_list.begin();
+       iter != window_list.end(); ++iter) {
+    if ((*iter)->web_contents() == web_contents)
+      return (*iter)->GetNativeWindow();
+  }
+
+  return NULL;
+}
+#endif
+
 }  // namespace
 
 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
@@ -264,17 +274,6 @@ MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
   notifications_registrar_.Add(
       this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
       content::NotificationService::AllSources());
-
-  // AVFoundation is used for video/audio device monitoring and video capture in
-  // Mac. Experimentally, connect it in Canary and Unkown (developer builds).
-#if defined(OS_MACOSX)
-  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
-  if (channel == chrome::VersionInfo::CHANNEL_CANARY ||
-      channel == chrome::VersionInfo::CHANNEL_UNKNOWN) {
-    CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableAVFoundation);
-  }
-#endif
 }
 
 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
@@ -381,11 +380,24 @@ void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest(
   // The extension name that the stream is registered with.
   std::string original_extension_name;
   // Resolve DesktopMediaID for the specified device id.
-  content::DesktopMediaID media_id =
-      GetDesktopStreamsRegistry()->RequestMediaForStreamId(
-          request.requested_video_device_id, request.render_process_id,
-          request.render_view_id, request.security_origin,
-          &original_extension_name);
+  content::DesktopMediaID media_id;
+  // TODO(miu): Replace "main RenderFrame" IDs with the request's actual
+  // RenderFrame IDs once the desktop capture extension API implementation is
+  // fixed.  http://crbug.com/304341
+  content::WebContents* const web_contents_for_stream =
+      content::WebContents::FromRenderFrameHost(
+          content::RenderFrameHost::FromID(request.render_process_id,
+                                           request.render_frame_id));
+  content::RenderFrameHost* const main_frame = web_contents_for_stream ?
+      web_contents_for_stream->GetMainFrame() : NULL;
+  if (main_frame) {
+    media_id = GetDesktopStreamsRegistry()->RequestMediaForStreamId(
+        request.requested_video_device_id,
+        main_frame->GetProcess()->GetID(),
+        main_frame->GetRoutingID(),
+        request.security_origin,
+        &original_extension_name);
+  }
 
   // Received invalid device id.
   if (media_id.type == content::DesktopMediaID::TYPE_NONE) {
@@ -435,7 +447,8 @@ void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
   const bool screen_capture_enabled =
       CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableUserMediaScreenCapturing) ||
-      IsOriginWhitelistedForScreenCapture(request.security_origin);
+      IsOriginForCasting(request.security_origin) ||
+      IsBuiltInExtension(request.security_origin);
 
   const bool origin_is_secure =
       request.security_origin.SchemeIsSecure() ||
@@ -454,6 +467,12 @@ void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
     // is closed. See http://crbug.com/326690.
     base::string16 application_title =
         GetApplicationTitle(web_contents, extension);
+#if !defined(OS_ANDROID)
+    gfx::NativeWindow parent_window =
+        FindParentWindowForWebContents(web_contents);
+#else
+    gfx::NativeWindow parent_window = NULL;
+#endif
     web_contents = NULL;
 
     // For component extensions, bypass message box.
@@ -467,7 +486,7 @@ void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
               IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
           application_name);
       chrome::MessageBoxResult result = chrome::ShowMessageBox(
-          NULL,
+          parent_window,
           l10n_util::GetStringFUTF16(
               IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
           confirmation_text,
@@ -515,10 +534,7 @@ void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
   content::MediaStreamDevices devices;
   scoped_ptr<content::MediaStreamUI> ui;
 
-#if defined(OS_ANDROID)
-  // Tab capture is not supported on Android.
-  callback.Run(devices, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE, ui.Pass());
-#else  // defined(OS_ANDROID)
+#if defined(ENABLE_EXTENSIONS)
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   extensions::TabCaptureRegistry* tab_capture_registry =
@@ -528,20 +544,21 @@ void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
     callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
     return;
   }
-  bool tab_capture_allowed =
-      tab_capture_registry->VerifyRequest(request.render_process_id,
-                                          request.render_view_id);
+  const bool tab_capture_allowed = tab_capture_registry->VerifyRequest(
+      request.render_process_id, request.render_frame_id, extension->id());
 
   if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
       tab_capture_allowed &&
-      extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
+      extension->permissions_data()->HasAPIPermission(
+          extensions::APIPermission::kTabCapture)) {
     devices.push_back(content::MediaStreamDevice(
         content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
   }
 
   if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
       tab_capture_allowed &&
-      extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
+      extension->permissions_data()->HasAPIPermission(
+          extensions::APIPermission::kTabCapture)) {
     devices.push_back(content::MediaStreamDevice(
         content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
   }
@@ -555,7 +572,9 @@ void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
     devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
                       content::MEDIA_DEVICE_OK,
     ui.Pass());
-#endif  // !defined(OS_ANDROID)
+#else  // defined(ENABLE_EXTENSIONS)
+  callback.Run(devices, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE, ui.Pass());
+#endif  // defined(ENABLE_EXTENSIONS)
 }
 
 void MediaCaptureDevicesDispatcher::
@@ -564,18 +583,52 @@ void MediaCaptureDevicesDispatcher::
         const content::MediaStreamRequest& request,
         const content::MediaResponseCallback& callback,
         const extensions::Extension* extension) {
+
+  // TODO(vrk): This code is largely duplicated in
+  // MediaStreamDevicesController::Accept(). Move this code into a shared method
+  // between the two classes.
+
+  bool audio_allowed =
+      request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
+      extension->permissions_data()->HasAPIPermission(
+          extensions::APIPermission::kAudioCapture);
+  bool video_allowed =
+      request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
+      extension->permissions_data()->HasAPIPermission(
+          extensions::APIPermission::kVideoCapture);
+
+  bool get_default_audio_device = audio_allowed;
+  bool get_default_video_device = video_allowed;
+
   content::MediaStreamDevices devices;
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
-  if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
-      extension->HasAPIPermission(extensions::APIPermission::kAudioCapture)) {
-    GetDefaultDevicesForProfile(profile, true, false, &devices);
+  // Get the exact audio or video device if an id is specified.
+  if (audio_allowed && !request.requested_audio_device_id.empty()) {
+    const content::MediaStreamDevice* audio_device =
+        GetRequestedAudioDevice(request.requested_audio_device_id);
+    if (audio_device) {
+      devices.push_back(*audio_device);
+      get_default_audio_device = false;
+    }
+  }
+  if (video_allowed && !request.requested_video_device_id.empty()) {
+    const content::MediaStreamDevice* video_device =
+        GetRequestedVideoDevice(request.requested_video_device_id);
+    if (video_device) {
+      devices.push_back(*video_device);
+      get_default_video_device = false;
+    }
   }
 
-  if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
-      extension->HasAPIPermission(extensions::APIPermission::kVideoCapture)) {
-    GetDefaultDevicesForProfile(profile, false, true, &devices);
+  // If either or both audio and video devices were requested but not
+  // specified by id, get the default devices.
+  if (get_default_audio_device || get_default_video_device) {
+    Profile* profile =
+        Profile::FromBrowserContext(web_contents->GetBrowserContext());
+    GetDefaultDevicesForProfile(profile,
+                                get_default_audio_device,
+                                get_default_video_device,
+                                &devices);
   }
 
   scoped_ptr<content::MediaStreamUI> ui;
@@ -618,7 +671,8 @@ void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
 
   DCHECK(!it->second.empty());
 
-  if (PermissionBubbleManager::Enabled()) {
+  if (PermissionBubbleManager::Enabled() ||
+      MediaStreamPermissionBubbleExperimentEnabled()) {
     scoped_ptr<MediaStreamDevicesController> controller(
         new MediaStreamDevicesController(web_contents,
             it->second.front().request,
@@ -626,8 +680,10 @@ void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
                        base::Unretained(this), web_contents)));
     if (controller->DismissInfoBarAndTakeActionOnSettings())
       return;
-    PermissionBubbleManager::FromWebContents(web_contents)->
-        AddRequest(controller.release());
+    PermissionBubbleManager* bubble_manager =
+        PermissionBubbleManager::FromWebContents(web_contents);
+    if (bubble_manager)
+      bubble_manager->AddRequest(controller.release());
     return;
   }
 
@@ -778,18 +834,18 @@ void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {
 
 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
     int render_process_id,
-    int render_view_id,
+    int render_frame_id,
     int page_request_id,
     const GURL& security_origin,
-    const content::MediaStreamDevice& device,
+    content::MediaStreamType stream_type,
     content::MediaRequestState state) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::Bind(
           &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
-          base::Unretained(this), render_process_id, render_view_id,
-          page_request_id, security_origin, device, state));
+          base::Unretained(this), render_process_id, render_frame_id,
+          page_request_id, security_origin, stream_type, state));
 }
 
 void MediaCaptureDevicesDispatcher::OnAudioStreamPlaying(
@@ -851,17 +907,17 @@ void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
 
 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
     int render_process_id,
-    int render_view_id,
+    int render_frame_id,
     int page_request_id,
     const GURL& security_origin,
-    const content::MediaStreamDevice& device,
+    content::MediaStreamType stream_type,
     content::MediaRequestState state) {
   // Track desktop capture sessions.  Tracking is necessary to avoid unbalanced
   // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE,
   // but they will all reach MEDIA_REQUEST_STATE_CLOSING.
-  if (device.type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
+  if (stream_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
     if (state == content::MEDIA_REQUEST_STATE_DONE) {
-      DesktopCaptureSession session = { render_process_id, render_view_id,
+      DesktopCaptureSession session = { render_process_id, render_frame_id,
                                         page_request_id };
       desktop_capture_sessions_.push_back(session);
     } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
@@ -870,7 +926,7 @@ void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
            it != desktop_capture_sessions_.end();
            ++it) {
         if (it->render_process_id == render_process_id &&
-            it->render_view_id == render_view_id &&
+            it->render_frame_id == render_frame_id &&
             it->page_request_id == page_request_id) {
           desktop_capture_sessions_.erase(it);
           break;
@@ -888,7 +944,7 @@ void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
       for (RequestsQueue::iterator it = queue.begin();
            it != queue.end(); ++it) {
         if (it->request.render_process_id == render_process_id &&
-            it->request.render_view_id == render_view_id &&
+            it->request.render_frame_id == render_frame_id &&
             it->request.page_request_id == page_request_id) {
           queue.erase(it);
           found = true;
@@ -901,7 +957,7 @@ void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
   }
 
 #if defined(OS_CHROMEOS)
-  if (IsOriginForCasting(security_origin) && IsVideoMediaType(device.type)) {
+  if (IsOriginForCasting(security_origin) && IsVideoMediaType(stream_type)) {
     // Notify ash that casting state has changed.
     if (state == content::MEDIA_REQUEST_STATE_DONE) {
       ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true);
@@ -913,8 +969,8 @@ void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
 
   FOR_EACH_OBSERVER(Observer, observers_,
                     OnRequestUpdate(render_process_id,
-                                    render_view_id,
-                                    device,
+                                    render_frame_id,
+                                    stream_type,
                                     state));
 }