summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/chrome_qt.gyp18
-rw-r--r--src/core/content_browser_client_qt.cpp7
-rw-r--r--src/core/content_browser_client_qt.h1
-rw-r--r--src/core/core_gyp_generator.pro2
-rw-r--r--src/core/media_capture_devices_dispatcher.cpp511
-rw-r--r--src/core/media_capture_devices_dispatcher.h159
-rw-r--r--src/core/qtwebengine.gypi1
-rw-r--r--src/core/web_contents_adapter.cpp7
-rw-r--r--src/core/web_contents_adapter.h1
-rw-r--r--src/core/web_contents_adapter_client.h9
-rw-r--r--src/core/web_contents_delegate_qt.cpp6
-rw-r--r--src/core/web_contents_delegate_qt.h1
-rw-r--r--src/webengine/api/qquickwebengineview_p_p.h1
-rw-r--r--src/webenginewidgets/api/qwebenginepage_p.h1
14 files changed, 725 insertions, 0 deletions
diff --git a/src/core/chrome_qt.gyp b/src/core/chrome_qt.gyp
new file mode 100644
index 000000000..a38b4a9bf
--- /dev/null
+++ b/src/core/chrome_qt.gyp
@@ -0,0 +1,18 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'chrome_qt',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(chromium_src_dir)',
+ '<(chromium_src_dir)/skia/config',
+ ],
+ 'sources': [
+ '<(chromium_src_dir)/chrome/browser/media/desktop_streams_registry.h',
+ '<(chromium_src_dir)/chrome/browser/media/desktop_streams_registry.cc',
+ '<(chromium_src_dir)/chrome/browser/media/desktop_media_list.h',
+ ],
+ }
+ ],
+}
+
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index 86ff11efb..8945ad0e5 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -45,6 +45,7 @@
#include "base/threading/thread_restrictions.h"
#include "content/public/browser/browser_main_parts.h"
#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/media_observer.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/common/main_function_params.h"
#include "content/public/common/url_constants.h"
@@ -54,6 +55,7 @@
#include "browser_context_qt.h"
#include "dev_tools_http_handler_delegate_qt.h"
+#include "media_capture_devices_dispatcher.h"
#include "resource_dispatcher_host_delegate_qt.h"
#include "web_contents_view_qt.h"
@@ -310,6 +312,11 @@ gfx::GLShareGroup *ContentBrowserClientQt::GetInProcessGpuShareGroup()
return m_shareGroupQtQuick.get();
}
+content::MediaObserver *ContentBrowserClientQt::GetMediaObserver()
+{
+ return MediaCaptureDevicesDispatcher::GetInstance();
+}
+
BrowserContextQt* ContentBrowserClientQt::browser_context() {
Q_ASSERT(m_browserMainParts);
return static_cast<BrowserMainPartsQt*>(m_browserMainParts)->browser_context();
diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h
index c8afc633a..f511eb934 100644
--- a/src/core/content_browser_client_qt.h
+++ b/src/core/content_browser_client_qt.h
@@ -82,6 +82,7 @@ public:
virtual void RenderProcessHostCreated(content::RenderProcessHost* host) Q_DECL_OVERRIDE;
virtual void ResourceDispatcherHostCreated() Q_DECL_OVERRIDE;
virtual gfx::GLShareGroup* GetInProcessGpuShareGroup() Q_DECL_OVERRIDE;
+ virtual content::MediaObserver* GetMediaObserver();
BrowserContextQt* browser_context();
diff --git a/src/core/core_gyp_generator.pro b/src/core/core_gyp_generator.pro
index be9bf87c0..c76c707a5 100644
--- a/src/core/core_gyp_generator.pro
+++ b/src/core/core_gyp_generator.pro
@@ -45,6 +45,7 @@ SOURCES = \
gl_context_qt.cpp \
javascript_dialog_controller.cpp \
javascript_dialog_manager_qt.cpp \
+ media_capture_devices_dispatcher.cpp \
process_main.cpp \
qt_render_view_observer_host.cpp \
render_widget_host_view_qt.cpp \
@@ -85,6 +86,7 @@ HEADERS = \
javascript_dialog_controller_p.h \
javascript_dialog_controller.h \
javascript_dialog_manager_qt.h \
+ media_capture_devices_dispatcher.h \
process_main.h \
qt_render_view_observer_host.h \
render_widget_host_view_qt.h \
diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp
new file mode 100644
index 000000000..2997576ff
--- /dev/null
+++ b/src/core/media_capture_devices_dispatcher.cpp
@@ -0,0 +1,511 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+// 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 "media_capture_devices_dispatcher.h"
+
+#include "javascript_dialog_manager_qt.h"
+#include "type_conversion.h"
+#include "web_contents_view_qt.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/media/desktop_streams_registry.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/desktop_media_id.h"
+#include "content/public/browser/media_devices_monitor.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/media_stream_request.h"
+#include "media/audio/audio_manager_base.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using content::BrowserThread;
+using content::MediaStreamDevices;
+
+namespace {
+
+const content::MediaStreamDevice *findDeviceWithId(const content::MediaStreamDevices &devices, const std::string &deviceId)
+{
+ content::MediaStreamDevices::const_iterator iter = devices.begin();
+ for (; iter != devices.end(); ++iter) {
+ if (iter->id == deviceId) {
+ return &(*iter);
+ }
+ }
+ return 0;
+}
+
+base::string16 getContentsUrl(content::WebContents *webContents)
+{
+ return UTF8ToUTF16(webContents->GetURL().GetOrigin().spec());
+}
+
+scoped_ptr<content::MediaStreamUI> getDevicesForDesktopCapture(content::MediaStreamDevices &devices, content::DesktopMediaID mediaId
+ , bool captureAudio, bool /*display_notification*/, base::string16 /*application_title*/)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ scoped_ptr<content::MediaStreamUI> ui;
+
+ // Add selected desktop source to the list.
+ devices.push_back(content::MediaStreamDevice(
+ content::MEDIA_DESKTOP_VIDEO_CAPTURE, mediaId.ToString(), "Screen"));
+ if (captureAudio) {
+ // Use the special loopback device ID for system audio capture.
+ devices.push_back(content::MediaStreamDevice(
+ content::MEDIA_LOOPBACK_AUDIO_CAPTURE,
+ media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio"));
+ }
+
+ return ui.Pass();
+}
+
+WebContentsAdapterClient::MediaRequestFlags mediaRequestFlagsForRequest(const content::MediaStreamRequest &request)
+{
+ WebContentsAdapterClient::MediaRequestFlags requestFlags;
+ if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE)
+ requestFlags |= WebContentsAdapterClient::MediaAudioCapture;
+ if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE)
+ requestFlags |= WebContentsAdapterClient::MediaVideoCapture;
+ return requestFlags;
+}
+
+} // namespace
+
+MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(const content::MediaStreamRequest &request
+ , const content::MediaResponseCallback &callback)
+ : request(request)
+ , callback(callback)
+{
+}
+
+MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest()
+{
+}
+
+
+void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content::WebContents *webContents, const QUrl &securityOrigin
+ , WebContentsAdapterClient::MediaRequestFlags authorizationFlags)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ content::MediaStreamDevices devices;
+ std::map<content::WebContents*, RequestsQueue>::iterator it = m_pendingRequests.find(webContents);
+
+ if (it == m_pendingRequests.end())
+ // WebContents has been destroyed. Don't need to do anything.
+ return;
+
+ RequestsQueue &queue(it->second);
+ if (queue.empty())
+ return;
+
+ content::MediaStreamRequest &request = queue.front().request;
+
+ const QUrl requestSecurityOrigin(toQt(request.security_origin));
+ bool securityOriginsMatch = (requestSecurityOrigin.host() == securityOrigin.host()
+ && requestSecurityOrigin.scheme() == securityOrigin.scheme()
+ && requestSecurityOrigin.port() == securityOrigin.port());
+ if (!securityOriginsMatch)
+ qWarning("Security origin mismatch for media access permission: %s requested and %s provided\n", qPrintable(requestSecurityOrigin.toString())
+ , qPrintable(securityOrigin.toString()));
+
+
+ bool microphoneRequested =
+ (request.audio_type && authorizationFlags & WebContentsAdapterClient::MediaAudioCapture);
+ bool webcamRequested =
+ (request.video_type && authorizationFlags & WebContentsAdapterClient::MediaVideoCapture);
+ if (securityOriginsMatch && (microphoneRequested || webcamRequested)) {
+ switch (request.request_type) {
+ case content::MEDIA_OPEN_DEVICE:
+ Q_UNREACHABLE(); // only speculative as this is for Pepper
+ getDefaultDevices("", "", microphoneRequested, webcamRequested, &devices);
+ break;
+ case content::MEDIA_DEVICE_ACCESS:
+ case content::MEDIA_GENERATE_STREAM:
+ case content::MEDIA_ENUMERATE_DEVICES:
+ getDefaultDevices(request.requested_audio_device_id, request.requested_video_device_id,
+ microphoneRequested, webcamRequested, &devices);
+ break;
+ }
+ }
+
+ content::MediaResponseCallback callback = queue.front().callback;
+ queue.pop_front();
+
+ if (!queue.empty()) {
+ // Post a task to process next queued request. It has to be done
+ // asynchronously to make sure that calling infobar is not destroyed until
+ // after this function returns.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, base::Unretained(this), webContents));
+ }
+
+ callback.Run(devices, scoped_ptr<content::MediaStreamUI>());
+}
+
+
+
+MediaCaptureDevicesDispatcher *MediaCaptureDevicesDispatcher::GetInstance()
+{
+ return Singleton<MediaCaptureDevicesDispatcher>::get();
+}
+
+MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
+ : m_devicesEnumerated(false)
+{
+ // MediaCaptureDevicesDispatcher is a singleton. It should be created on
+ // UI thread. Otherwise, it will not receive
+ // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in
+ // possible use after free.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ m_notificationsRegistrar.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+ content::NotificationService::AllSources());
+}
+
+MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher()
+{
+}
+
+const MediaStreamDevices &MediaCaptureDevicesDispatcher::getAudioCaptureDevices()
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!m_devicesEnumerated) {
+ content::EnsureMonitorCaptureDevices();
+ m_devicesEnumerated = true;
+ }
+ return m_audioDevices;
+}
+
+const MediaStreamDevices &MediaCaptureDevicesDispatcher::getVideoCaptureDevices()
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!m_devicesEnumerated) {
+ content::EnsureMonitorCaptureDevices();
+ m_devicesEnumerated = true;
+ }
+ return m_videoDevices;
+}
+
+void MediaCaptureDevicesDispatcher::Observe(int type, const content::NotificationSource &source, const content::NotificationDetails &details)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
+ content::WebContents *webContents = content::Source<content::WebContents>(source).ptr();
+ m_pendingRequests.erase(webContents);
+ }
+}
+
+void MediaCaptureDevicesDispatcher::processMediaAccessRequest(WebContentsAdapterClient *adapterClient, content::WebContents *webContents
+ , const content::MediaStreamRequest &request
+ , const content::MediaResponseCallback &callback)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Let's not support tab capture for now.
+ if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE || request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE)
+ return;
+
+ if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE || request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE)
+ // It's still unclear what to make of screen capture. We can rely on existing javascript dialog infrastructure
+ // to experiment with this without exposing it through our API yet.
+ processDesktopCaptureAccessRequest(webContents, request, callback);
+ else {
+ enqueueMediaAccessRequest(webContents, request, callback);
+ // We might not require this approval for pepper requests.
+ adapterClient->runMediaAccessPermissionRequest(toQt(request.security_origin), mediaRequestFlagsForRequest(request));
+ }
+
+}
+
+void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content::WebContents *webContents, const content::MediaStreamRequest &request
+ , const content::MediaResponseCallback &callback)
+{
+ content::MediaStreamDevices devices;
+ scoped_ptr<content::MediaStreamUI> ui;
+
+ if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
+ callback.Run(devices, ui.Pass());
+ return;
+ }
+
+ // If the device id wasn't specified then this is a screen capture request
+ // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
+ if (request.requested_video_device_id.empty()) {
+ processScreenCaptureAccessRequest(
+ webContents, request, callback);
+ return;
+ }
+
+ // Resolve DesktopMediaID for the specified device id.
+ content::DesktopMediaID mediaId =
+ getDesktopStreamsRegistry()->RequestMediaForStreamId(
+ request.requested_video_device_id, request.render_process_id,
+ request.render_view_id, request.security_origin);
+
+ // Received invalid device id.
+ if (mediaId.type == content::DesktopMediaID::TYPE_NONE) {
+ callback.Run(devices, ui.Pass());
+ return;
+ }
+
+ // Audio is only supported for screen capture streams.
+ bool capture_audio = (mediaId.type == content::DesktopMediaID::TYPE_SCREEN &&
+ request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE);
+
+ ui = getDevicesForDesktopCapture(
+ devices, mediaId, capture_audio, true,
+ getContentsUrl(webContents));
+
+ callback.Run(devices, ui.Pass());
+}
+
+void MediaCaptureDevicesDispatcher::processScreenCaptureAccessRequest(content::WebContents *webContents, const content::MediaStreamRequest &request
+ ,const content::MediaResponseCallback &callback)
+{
+ DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE);
+
+ // FIXME: expose through the settings once we have them
+ const bool screenCaptureEnabled = !qgetenv("QT_WEBENGINE_USE_EXPERIMENTAL_SCREEN_CAPTURE").isNull();
+
+ const bool originIsSecure = request.security_origin.SchemeIsSecure();
+
+ if (screenCaptureEnabled && originIsSecure) {
+
+ enqueueMediaAccessRequest(webContents, request, callback);
+ base::Callback<void(bool, const base::string16&)> dialogCallback = base::Bind(&MediaCaptureDevicesDispatcher::handleScreenCaptureAccessRequest,
+ base::Unretained(this), base::Unretained(webContents));
+
+ QUrl securityOrigin(toQt(request.security_origin));
+ QString message = QObject::tr("Do you want %1 to share your screen?").arg(securityOrigin.toString());
+ QString title = QObject::tr("%1 Screen Sharing request").arg(securityOrigin.toString());
+ JavaScriptDialogManagerQt::GetInstance()->runDialogForContents(webContents, WebContentsAdapterClient::InternalAuthorizationDialog, message
+ , QString(), securityOrigin, dialogCallback, title);
+ } else
+ callback.Run(content::MediaStreamDevices(), scoped_ptr<content::MediaStreamUI>());
+}
+
+void MediaCaptureDevicesDispatcher::handleScreenCaptureAccessRequest(content::WebContents *webContents, bool userAccepted, const base::string16 &)
+{
+ content::MediaStreamDevices devices;
+ scoped_ptr<content::MediaStreamUI> ui;
+ if (userAccepted) {
+ content::DesktopMediaID screenId = content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, 0);
+ ui = getDevicesForDesktopCapture(devices, screenId, false/*capture_audio*/, false/*display_notification*/, getContentsUrl(webContents));
+ }
+ std::map<content::WebContents*, RequestsQueue>::iterator it =
+ m_pendingRequests.find(webContents);
+ if (it == m_pendingRequests.end()) {
+ // WebContents has been destroyed. Don't need to do anything.
+ return;
+ }
+
+ RequestsQueue &queue(it->second);
+ if (queue.empty())
+ return;
+
+ content::MediaResponseCallback callback = queue.front().callback;
+ queue.pop_front();
+
+ callback.Run(devices, ui.Pass());
+}
+
+void MediaCaptureDevicesDispatcher::enqueueMediaAccessRequest(content::WebContents *webContents, const content::MediaStreamRequest &request
+ ,const content::MediaResponseCallback &callback)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ RequestsQueue &queue = m_pendingRequests[webContents];
+ queue.push_back(PendingAccessRequest(request, callback));
+}
+
+void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(content::WebContents *webContents) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ std::map<content::WebContents*, RequestsQueue>::iterator it =
+ m_pendingRequests.find(webContents);
+
+ if (it == m_pendingRequests.end() || it->second.empty())
+ return;
+
+ RequestsQueue &queue(it->second);
+ if (queue.empty())
+ return;
+
+ content::MediaStreamRequest &request = queue.front().request;
+
+ DCHECK(!it->second.empty());
+ WebContentsAdapterClient *adapterClient = WebContentsViewQt::from(webContents->GetView())->client();
+ adapterClient->runMediaAccessPermissionRequest(toQt(request.security_origin), mediaRequestFlagsForRequest(request));
+}
+
+void MediaCaptureDevicesDispatcher::getDefaultDevices(const std::string &audioDeviceId, const std::string &videoDeviceId, bool audio, bool video
+ , content::MediaStreamDevices *devices)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(audio || video);
+
+ if (audio) {
+ const content::MediaStreamDevices &audioDevices = getAudioCaptureDevices();
+ const content::MediaStreamDevice *device = findDeviceWithId(audioDevices, audioDeviceId);
+ if (!device && !audioDevices.empty())
+ device = &(*audioDevices.begin());
+ if (device)
+ devices->push_back(*device);
+ }
+
+ if (video) {
+ const content::MediaStreamDevices &videoDevices = getVideoCaptureDevices();
+ const content::MediaStreamDevice *device = findDeviceWithId(videoDevices, videoDeviceId);
+ if (!device && !videoDevices.empty())
+ device = &(*videoDevices.begin());
+ if (device)
+ devices->push_back(*device);
+ }
+}
+
+DesktopStreamsRegistry *MediaCaptureDevicesDispatcher::getDesktopStreamsRegistry()
+{
+ if (!m_desktopStreamsRegistry)
+ m_desktopStreamsRegistry.reset(new DesktopStreamsRegistry());
+ return m_desktopStreamsRegistry.get();
+}
+
+void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged(const content::MediaStreamDevices &devices)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MediaCaptureDevicesDispatcher::updateAudioDevicesOnUIThread,
+ base::Unretained(this), devices));
+}
+
+void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged(const content::MediaStreamDevices &devices)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MediaCaptureDevicesDispatcher::updateVideoDevicesOnUIThread,
+ base::Unretained(this), devices));
+}
+
+void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(int renderProcessId, int renderViewId, int pageRequestId
+ , const content::MediaStreamDevice &device, content::MediaRequestState state)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread,
+ base::Unretained(this), renderProcessId, renderViewId,
+ pageRequestId, device, state));
+}
+
+void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(int /*renderProcessId*/, int /*renderViewId*/)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+}
+
+void MediaCaptureDevicesDispatcher::updateAudioDevicesOnUIThread(const content::MediaStreamDevices &devices)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ m_devicesEnumerated = true;
+ m_audioDevices = devices;
+}
+
+void MediaCaptureDevicesDispatcher::updateVideoDevicesOnUIThread(const content::MediaStreamDevices &devices)
+{
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ m_devicesEnumerated = true;
+ m_videoDevices = devices;
+}
+
+void MediaCaptureDevicesDispatcher::updateMediaRequestStateOnUIThread(int renderProcessId, int renderViewId, int pageRequestId
+ , const content::MediaStreamDevice &device, 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 (state == content::MEDIA_REQUEST_STATE_DONE) {
+ DesktopCaptureSession session = { renderProcessId, renderViewId,
+ pageRequestId };
+ m_desktopCaptureSessions.push_back(session);
+ } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
+ for (DesktopCaptureSessions::iterator it =
+ m_desktopCaptureSessions.begin();
+ it != m_desktopCaptureSessions.end();
+ ++it) {
+ if (it->render_process_id == renderProcessId &&
+ it->render_view_id == renderViewId &&
+ it->page_request_id == pageRequestId) {
+ m_desktopCaptureSessions.erase(it);
+ break;
+ }
+ }
+ }
+ }
+
+ // Cancel the request.
+ if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
+ bool found = false;
+ for (RequestsQueues::iterator rqs_it = m_pendingRequests.begin();
+ rqs_it != m_pendingRequests.end(); ++rqs_it) {
+ RequestsQueue &queue = rqs_it->second;
+ for (RequestsQueue::iterator it = queue.begin();
+ it != queue.end(); ++it) {
+ if (it->request.render_process_id == renderProcessId &&
+ it->request.render_view_id == renderViewId &&
+ it->request.page_request_id == pageRequestId) {
+ queue.erase(it);
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ }
+}
diff --git a/src/core/media_capture_devices_dispatcher.h b/src/core/media_capture_devices_dispatcher.h
new file mode 100644
index 000000000..38d7b1c19
--- /dev/null
+++ b/src/core/media_capture_devices_dispatcher.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (c) 2012 The Chromium Authors. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MEDIA_CAPTURE_DEVICES_DISPATCHER_H
+#define MEDIA_CAPTURE_DEVICES_DISPATCHER_H
+
+#include <deque>
+#include <list>
+#include <map>
+#include <QtCore/qcompilerdetection.h>
+
+#include "web_contents_adapter_client.h"
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list.h"
+#include "content/public/browser/media_observer.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/media_stream_request.h"
+
+class AudioStreamIndicator;
+class DesktopStreamsRegistry;
+class MediaStreamCaptureIndicator;
+
+// This singleton is used to receive updates about media events from the content
+// layer. Based on Chrome's implementation.
+class MediaCaptureDevicesDispatcher : public content::MediaObserver,
+ public content::NotificationObserver {
+ public:
+
+ static MediaCaptureDevicesDispatcher *GetInstance();
+
+ void processMediaAccessRequest(WebContentsAdapterClient *, content::WebContents *, const content::MediaStreamRequest &, const content::MediaResponseCallback &);
+
+ // Called back from our WebContentsAdapter to grant the requested permission.
+ void handleMediaAccessPermissionResponse(content::WebContents *, const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags);
+
+ void getDefaultDevices(const std::string &audioDeviceId, const std::string &videoDeviceId, bool audio, bool video, content::MediaStreamDevices *);
+
+ // Overridden from content::MediaObserver:
+ virtual void OnAudioCaptureDevicesChanged(const content::MediaStreamDevices &) Q_DECL_OVERRIDE;
+ virtual void OnVideoCaptureDevicesChanged(const content::MediaStreamDevices &) Q_DECL_OVERRIDE;
+ virtual void OnMediaRequestStateChanged(int renderProcessId, int renderViewId, int pageRequestId, const content::MediaStreamDevice &device
+ , content::MediaRequestState state) Q_DECL_OVERRIDE;
+ virtual void OnAudioStreamPlayingChanged(int /*render_process_id*/, int /*render_view_id*/, int /*stream_id*/
+ , bool /*is_playing*/, float /*power_dBFS*/, bool /*clipped*/) Q_DECL_OVERRIDE {}
+ virtual void OnCreatingAudioStream(int renderProcessId, int renderViewId) Q_DECL_OVERRIDE;
+
+ DesktopStreamsRegistry *getDesktopStreamsRegistry();
+
+ private:
+ friend struct DefaultSingletonTraits<MediaCaptureDevicesDispatcher>;
+
+ struct PendingAccessRequest {
+ PendingAccessRequest(const content::MediaStreamRequest &request,
+ const content::MediaResponseCallback &callback);
+ ~PendingAccessRequest();
+
+ content::MediaStreamRequest request;
+ content::MediaResponseCallback callback;
+ };
+ typedef std::deque<PendingAccessRequest> RequestsQueue;
+ typedef std::map<content::WebContents *, RequestsQueue> RequestsQueues;
+
+ MediaCaptureDevicesDispatcher();
+ virtual ~MediaCaptureDevicesDispatcher();
+
+ const content::MediaStreamDevices &getAudioCaptureDevices();
+ const content::MediaStreamDevices &getVideoCaptureDevices();
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type, const content::NotificationSource &source, const content::NotificationDetails &details) Q_DECL_OVERRIDE;
+
+ // Helpers for ProcessMediaAccessRequest().
+ void processDesktopCaptureAccessRequest(content::WebContents *, const content::MediaStreamRequest &, const content::MediaResponseCallback &);
+ void processScreenCaptureAccessRequest(content::WebContents *,const content::MediaStreamRequest &, const content::MediaResponseCallback &);
+ void handleScreenCaptureAccessRequest(content::WebContents *, bool userAccepted, const base::string16 &/*unused callback_input*/);
+ void enqueueMediaAccessRequest(content::WebContents *, const content::MediaStreamRequest &, const content::MediaResponseCallback &);
+ void ProcessQueuedAccessRequest(content::WebContents *);
+
+ // Called by the MediaObserver() functions, executed on UI thread.
+ void updateAudioDevicesOnUIThread(const content::MediaStreamDevices &);
+ void updateVideoDevicesOnUIThread(const content::MediaStreamDevices &);
+ void updateMediaRequestStateOnUIThread(int renderProcessId, int renderViewId, int pageRequestId, const content::MediaStreamDevice &
+ , content::MediaRequestState);
+
+ // A list of cached audio capture devices.
+ content::MediaStreamDevices m_audioDevices;
+
+ // A list of cached video capture devices.
+ content::MediaStreamDevices m_videoDevices;
+
+ // Flag to indicate if device enumeration has been done/doing.
+ // Only accessed on UI thread.
+ bool m_devicesEnumerated;
+
+ RequestsQueues m_pendingRequests;
+
+ scoped_ptr<DesktopStreamsRegistry> m_desktopStreamsRegistry;
+
+ content::NotificationRegistrar m_notificationsRegistrar;
+
+ // Tracks MEDIA_DESKTOP_VIDEO_CAPTURE sessions which reach the
+ // MEDIA_REQUEST_STATE_DONE state. Sessions are remove when
+ // MEDIA_REQUEST_STATE_CLOSING is encountered.
+ struct DesktopCaptureSession {
+ int render_process_id;
+ int render_view_id;
+ int page_request_id;
+ };
+ typedef std::list<DesktopCaptureSession> DesktopCaptureSessions;
+ DesktopCaptureSessions m_desktopCaptureSessions;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaCaptureDevicesDispatcher);
+};
+
+#endif // MEDIA_CAPTURE_DEVICES_DISPATCHER_H
diff --git a/src/core/qtwebengine.gypi b/src/core/qtwebengine.gypi
index fee9d37dd..c01998c59 100644
--- a/src/core/qtwebengine.gypi
+++ b/src/core/qtwebengine.gypi
@@ -28,6 +28,7 @@
'<(chromium_src_dir)/v8/tools/gyp/v8.gyp:v8',
'<(chromium_src_dir)/webkit/glue/webkit_glue.gyp:*',
'<(chromium_src_dir)/third_party/WebKit/Source/web/web.gyp:webkit',
+ 'chrome_qt.gyp:*',
],
'include_dirs': [
'<(chromium_src_dir)',
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index ff0b9a83d..11b5f7cc2 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -48,6 +48,7 @@
#include "browser_context_qt.h"
#include "content_browser_client_qt.h"
#include "javascript_dialog_manager_qt.h"
+#include "media_capture_devices_dispatcher.h"
#include "qt_render_view_observer_host.h"
#include "type_conversion.h"
#include "web_contents_adapter_client.h"
@@ -676,6 +677,12 @@ void WebContentsAdapter::wasHidden()
d->webContents->WasHidden();
}
+void WebContentsAdapter::grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags)
+{
+ Q_D(WebContentsAdapter);
+ MediaCaptureDevicesDispatcher::GetInstance()->handleMediaAccessPermissionResponse(d->webContents.get(), securityOrigin, flags);
+}
+
void WebContentsAdapter::dpiScaleChanged()
{
Q_D(WebContentsAdapter);
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index fc6ea99fb..b6984933e 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -106,6 +106,7 @@ public:
void wasShown();
void wasHidden();
+ void grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags);
void dpiScaleChanged();
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 64b5fca38..44668b539 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -43,6 +43,7 @@
#include "qtwebenginecoreglobal.h"
+#include <QFlags>
#include <QRect>
#include <QSharedPointer>
#include <QString>
@@ -115,6 +116,13 @@ public:
Error
};
+ enum MediaRequestFlag {
+ MediaNone = 0,
+ MediaAudioCapture = 0x01,
+ MediaVideoCapture = 0x02,
+ };
+ Q_DECLARE_FLAGS(MediaRequestFlags, MediaRequestFlag)
+
virtual ~WebContentsAdapterClient() { }
virtual RenderWidgetHostViewQtDelegate* CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client) = 0;
@@ -145,6 +153,7 @@ public:
virtual void passOnFocus(bool reverse) = 0;
virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) = 0;
virtual void authenticationRequired(const QUrl &requestUrl, const QString &realm, bool isProxy, const QString &challengingHost, QString *outUser, QString *outPassword) = 0;
+ virtual void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) = 0;
};
#endif // WEB_CONTENTS_ADAPTER_CLIENT_H
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 6e19fd9ab..756646be6 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -45,6 +45,7 @@
#include "web_contents_delegate_qt.h"
+#include "media_capture_devices_dispatcher.h"
#include "type_conversion.h"
#include "web_contents_adapter.h"
#include "web_contents_adapter_client.h"
@@ -235,3 +236,8 @@ void WebContentsDelegateQt::FindReply(content::WebContents *source, int request_
if (final_update)
m_viewClient->didFindText(request_id, number_of_matches);
}
+
+void WebContentsDelegateQt::RequestMediaAccessPermission(content::WebContents *web_contents, const content::MediaStreamRequest &request, const content::MediaResponseCallback &callback)
+{
+ MediaCaptureDevicesDispatcher::GetInstance()->processMediaAccessRequest(m_viewClient, web_contents, request, callback);
+}
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index fe36088eb..255428778 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -80,6 +80,7 @@ public:
virtual void RunFileChooser(content::WebContents *, const content::FileChooserParams &params) Q_DECL_OVERRIDE;
virtual bool AddMessageToConsole(content::WebContents* source, int32 level, const base::string16& message, int32 line_no, const base::string16& source_id) Q_DECL_OVERRIDE;
virtual void FindReply(content::WebContents *source, int request_id, int number_of_matches, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) Q_DECL_OVERRIDE;
+ virtual void RequestMediaAccessPermission(content::WebContents* web_contents, const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback) Q_DECL_OVERRIDE;
private:
WebContentsAdapterClient *m_viewClient;
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 4cbca6594..cde8d297d 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -153,6 +153,7 @@ public:
virtual void passOnFocus(bool reverse) Q_DECL_OVERRIDE;
virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) Q_DECL_OVERRIDE;
virtual void authenticationRequired(const QUrl&, const QString&, bool, const QString&, QString*, QString*) Q_DECL_OVERRIDE { }
+ virtual void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) Q_DECL_OVERRIDE { }
void setDevicePixelRatio(qreal);
void adoptWebContents(WebContentsAdapter *webContents);
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index ddc7c51e7..0c4d3b850 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -136,6 +136,7 @@ public:
virtual void passOnFocus(bool reverse) Q_DECL_OVERRIDE { Q_UNUSED(reverse); };
virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) Q_DECL_OVERRIDE;
virtual void authenticationRequired(const QUrl &requestUrl, const QString &realm, bool isProxy, const QString &challengingHost, QString *outUser, QString *outPassword) Q_DECL_OVERRIDE;
+ virtual void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) Q_DECL_OVERRIDE {};
void updateAction(QWebEnginePage::WebAction) const;
void updateNavigationActions();