summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/mediastream/UserMediaRequest.cpp')
-rw-r--r--Source/WebCore/Modules/mediastream/UserMediaRequest.cpp264
1 files changed, 136 insertions, 128 deletions
diff --git a/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp b/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
index e438d35a2..5df3e9896 100644
--- a/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
+++ b/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2011 Ericsson AB. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
@@ -32,68 +32,44 @@
*/
#include "config.h"
+#include "UserMediaRequest.h"
#if ENABLE(MEDIA_STREAM)
-#include "UserMediaRequest.h"
-
-#include "Dictionary.h"
#include "Document.h"
+#include "DocumentLoader.h"
#include "ExceptionCode.h"
+#include "JSMediaStream.h"
+#include "JSOverconstrainedError.h"
+#include "MainFrame.h"
#include "MediaConstraintsImpl.h"
-#include "MediaStream.h"
-#include "MediaStreamCenter.h"
-#include "MediaStreamPrivate.h"
-#include "SecurityOrigin.h"
+#include "RealtimeMediaSourceCenter.h"
+#include "Settings.h"
#include "UserMediaController.h"
-#include <wtf/Functional.h>
-#include <wtf/MainThread.h>
namespace WebCore {
-static PassRefPtr<MediaConstraints> parseOptions(const Dictionary& options, const String& mediaType, ExceptionCode& ec)
-{
- RefPtr<MediaConstraints> constraints;
-
- Dictionary constraintsDictionary;
- bool ok = options.get(mediaType, constraintsDictionary);
- if (ok && !constraintsDictionary.isUndefinedOrNull())
- constraints = MediaConstraintsImpl::create(constraintsDictionary, ec);
- else {
- bool mediaRequested = false;
- options.get(mediaType, mediaRequested);
- if (mediaRequested)
- constraints = MediaConstraintsImpl::create();
- }
-
- return constraints.release();
-}
-
-PassRefPtr<UserMediaRequest> UserMediaRequest::create(ScriptExecutionContext* context, UserMediaController* controller, const Dictionary& options, PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback, PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback, ExceptionCode& ec)
+ExceptionOr<void> UserMediaRequest::start(Document& document, Ref<MediaConstraintsImpl>&& audioConstraints, Ref<MediaConstraintsImpl>&& videoConstraints, DOMPromise<IDLInterface<MediaStream>>&& promise)
{
- ASSERT(successCallback);
-
- RefPtr<MediaConstraints> audioConstraints = parseOptions(options, AtomicString("audio", AtomicString::ConstructFromLiteral), ec);
- if (ec)
- return nullptr;
+ auto* userMedia = UserMediaController::from(document.page());
+ if (!userMedia)
+ return Exception { NOT_SUPPORTED_ERR }; // FIXME: Why is it better to return an exception here instead of rejecting the promise as we do just below?
- RefPtr<MediaConstraints> videoConstraints = parseOptions(options, AtomicString("video", AtomicString::ConstructFromLiteral), ec);
- if (ec)
- return nullptr;
-
- if (!audioConstraints && !videoConstraints)
- return nullptr;
+ if (!audioConstraints->isValid() && !videoConstraints->isValid()) {
+ promise.reject(TypeError);
+ return { };
+ }
- return adoptRef(new UserMediaRequest(context, controller, audioConstraints.release(), videoConstraints.release(), successCallback, errorCallback));
+ adoptRef(*new UserMediaRequest(document, *userMedia, WTFMove(audioConstraints), WTFMove(videoConstraints), WTFMove(promise)))->start();
+ return { };
}
-UserMediaRequest::UserMediaRequest(ScriptExecutionContext* context, UserMediaController* controller, PassRefPtr<MediaConstraints> audioConstraints, PassRefPtr<MediaConstraints> videoConstraints, PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback, PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback)
- : ContextDestructionObserver(context)
- , m_audioConstraints(audioConstraints)
- , m_videoConstraints(videoConstraints)
- , m_controller(controller)
- , m_successCallback(successCallback)
- , m_errorCallback(errorCallback)
+UserMediaRequest::UserMediaRequest(Document& document, UserMediaController& controller, Ref<MediaConstraintsImpl>&& audioConstraints, Ref<MediaConstraintsImpl>&& videoConstraints, DOMPromise<IDLInterface<MediaStream>>&& promise)
+ : ContextDestructionObserver(&document)
+ , m_audioConstraints(WTFMove(audioConstraints))
+ , m_videoConstraints(WTFMove(videoConstraints))
+ , m_controller(&controller)
+ , m_promise(WTFMove(promise))
{
}
@@ -101,126 +77,158 @@ UserMediaRequest::~UserMediaRequest()
{
}
-SecurityOrigin* UserMediaRequest::securityOrigin() const
+SecurityOrigin* UserMediaRequest::userMediaDocumentOrigin() const
{
- if (m_scriptExecutionContext)
- return m_scriptExecutionContext->securityOrigin();
-
- return nullptr;
-}
-
-void UserMediaRequest::start()
-{
- // 1 - make sure the system is capable of supporting the audio and video constraints. We don't want to ask for
- // user permission if the constraints can not be suported.
- MediaStreamCenter::shared().validateRequestConstraints(this, m_audioConstraints, m_videoConstraints);
+ if (!m_scriptExecutionContext)
+ return nullptr;
+ return m_scriptExecutionContext->securityOrigin();
}
-
-void UserMediaRequest::constraintsValidated()
+SecurityOrigin* UserMediaRequest::topLevelDocumentOrigin() const
{
- if (m_controller)
- callOnMainThread(bind(&UserMediaRequest::requestPermission, this));
+ if (!m_scriptExecutionContext)
+ return nullptr;
+ return &m_scriptExecutionContext->topOrigin();
}
-void UserMediaRequest::requestPermission()
+static bool isSecure(DocumentLoader& documentLoader)
{
- // 2 - The constraints are valid, ask the user for access to media.
- if (m_controller)
- m_controller->requestPermission(this);
+ auto& response = documentLoader.response();
+ return response.url().protocolIs("https")
+ && response.certificateInfo()
+ && !response.certificateInfo()->containsNonRootSHA1SignedCertificate();
}
-void UserMediaRequest::userMediaAccessGranted()
+static bool canCallGetUserMedia(Document& document, String& errorMessage)
{
- callOnMainThread(bind(&UserMediaRequest::createMediaStream, this));
-}
+ bool requiresSecureConnection = document.settings().mediaCaptureRequiresSecureConnection();
+ if (requiresSecureConnection && !isSecure(*document.loader())) {
+ errorMessage = "Trying to call getUserMedia from an insecure document.";
+ return false;
+ }
-void UserMediaRequest::createMediaStream()
-{
- // 3 - the user granted access, ask platform to create the media stream descriptors.
- MediaStreamCenter::shared().createMediaStream(this, m_audioConstraints, m_videoConstraints);
+ auto& topDocument = document.topDocument();
+ if (&document != &topDocument) {
+ auto& topOrigin = topDocument.topOrigin();
+
+ if (!document.securityOrigin().isSameSchemeHostPort(topOrigin)) {
+ errorMessage = "Trying to call getUserMedia from a document with a different security origin than its top-level frame.";
+ return false;
+ }
+
+ for (auto* ancestorDocument = document.parentDocument(); ancestorDocument != &topDocument; ancestorDocument = ancestorDocument->parentDocument()) {
+ if (requiresSecureConnection && !isSecure(*ancestorDocument->loader())) {
+ errorMessage = "Trying to call getUserMedia from a document with an insecure parent frame.";
+ return false;
+ }
+
+ if (!ancestorDocument->securityOrigin().isSameSchemeHostPort(topOrigin)) {
+ errorMessage = "Trying to call getUserMedia from a document with a different security origin than its top-level frame.";
+ return false;
+ }
+ }
+ }
+
+ return true;
}
-void UserMediaRequest::userMediaAccessDenied()
+void UserMediaRequest::start()
{
- failedToCreateStreamWithPermissionError();
-}
+ if (!m_scriptExecutionContext || !m_controller) {
+ deny(MediaAccessDenialReason::OtherFailure, emptyString());
+ return;
+ }
-void UserMediaRequest::constraintsInvalid(const String& constraintName)
-{
- failedToCreateStreamWithConstraintsError(constraintName);
-}
+ Document& document = downcast<Document>(*m_scriptExecutionContext);
-void UserMediaRequest::didCreateStream(PassRefPtr<MediaStreamPrivate> privateStream)
-{
- if (!m_scriptExecutionContext || !m_successCallback)
+ // 10.2 - 6.3 Optionally, e.g., based on a previously-established user preference, for security reasons,
+ // or due to platform limitations, jump to the step labeled Permission Failure below.
+ String errorMessage;
+ if (!canCallGetUserMedia(document, errorMessage)) {
+ deny(MediaAccessDenialReason::PermissionDenied, emptyString());
+ document.domWindow()->printErrorMessage(errorMessage);
return;
+ }
- callOnMainThread(bind(&UserMediaRequest::callSuccessHandler, this, privateStream));
+ m_controller->requestUserMediaAccess(*this);
}
-void UserMediaRequest::callSuccessHandler(PassRefPtr<MediaStreamPrivate> privateStream)
+void UserMediaRequest::allow(const String& audioDeviceUID, const String& videoDeviceUID)
{
- // 4 - Create the MediaStream and pass it to the success callback.
- ASSERT(m_successCallback);
+ m_allowedAudioDeviceUID = audioDeviceUID;
+ m_allowedVideoDeviceUID = videoDeviceUID;
- RefPtr<MediaStream> stream = MediaStream::create(*m_scriptExecutionContext, privateStream);
+ RefPtr<UserMediaRequest> protectedThis = this;
+ RealtimeMediaSourceCenter::NewMediaStreamHandler callback = [this, protectedThis = WTFMove(protectedThis)](RefPtr<MediaStreamPrivate>&& privateStream) mutable {
+ if (!m_scriptExecutionContext)
+ return;
- Vector<RefPtr<MediaStreamTrack>> tracks = stream->getAudioTracks();
- for (auto iter = tracks.begin(); iter != tracks.end(); ++iter)
- (*iter)->applyConstraints(m_audioConstraints);
+ if (!privateStream) {
+ deny(MediaAccessDenialReason::HardwareError, emptyString());
+ return;
+ }
- tracks = stream->getVideoTracks();
- for (auto iter = tracks.begin(); iter != tracks.end(); ++iter)
- (*iter)->applyConstraints(m_videoConstraints);
-
- m_successCallback->handleEvent(stream.get());
-}
+ auto stream = MediaStream::create(*m_scriptExecutionContext, WTFMove(privateStream));
+ if (stream->getTracks().isEmpty()) {
+ deny(MediaAccessDenialReason::HardwareError, emptyString());
+ return;
+ }
-void UserMediaRequest::failedToCreateStreamWithConstraintsError(const String& constraintName)
-{
- ASSERT(!constraintName.isEmpty());
- if (!m_scriptExecutionContext)
- return;
+ for (auto& track : stream->getAudioTracks())
+ track->source().startProducingData();
- if (!m_errorCallback)
- return;
+ for (auto& track : stream->getVideoTracks())
+ track->source().startProducingData();
+
+ m_promise.resolve(stream);
+ };
- RefPtr<NavigatorUserMediaError> error = NavigatorUserMediaError::create(NavigatorUserMediaError::constraintNotSatisfiedErrorName(), constraintName);
- callOnMainThread(bind(&UserMediaRequest::callErrorHandler, this, error.release()));
+ RealtimeMediaSourceCenter::singleton().createMediaStream(WTFMove(callback), m_allowedAudioDeviceUID, m_allowedVideoDeviceUID, &m_audioConstraints.get(), &m_videoConstraints.get());
}
-void UserMediaRequest::failedToCreateStreamWithPermissionError()
+void UserMediaRequest::deny(MediaAccessDenialReason reason, const String& invalidConstraint)
{
if (!m_scriptExecutionContext)
return;
- if (!m_errorCallback)
- return;
-
- RefPtr<NavigatorUserMediaError> error = NavigatorUserMediaError::create(NavigatorUserMediaError::permissionDeniedErrorName(), emptyString());
- callOnMainThread(bind(&UserMediaRequest::callErrorHandler, this, error.release()));
-}
-
-void UserMediaRequest::callErrorHandler(PassRefPtr<NavigatorUserMediaError> prpError)
-{
- RefPtr<NavigatorUserMediaError> error = prpError;
-
- ASSERT(error);
-
- m_errorCallback->handleEvent(error.get());
+ switch (reason) {
+ case MediaAccessDenialReason::NoConstraints:
+ m_promise.reject(TypeError);
+ break;
+ case MediaAccessDenialReason::UserMediaDisabled:
+ m_promise.reject(SECURITY_ERR);
+ break;
+ case MediaAccessDenialReason::NoCaptureDevices:
+ m_promise.reject(NOT_FOUND_ERR);
+ break;
+ case MediaAccessDenialReason::InvalidConstraint:
+ m_promise.rejectType<IDLInterface<OverconstrainedError>>(OverconstrainedError::create(invalidConstraint, ASCIILiteral("Invalid constraint")).get());
+ break;
+ case MediaAccessDenialReason::HardwareError:
+ m_promise.reject(NotReadableError);
+ break;
+ case MediaAccessDenialReason::OtherFailure:
+ m_promise.reject(ABORT_ERR);
+ break;
+ case MediaAccessDenialReason::PermissionDenied:
+ m_promise.reject(NotAllowedError);
+ break;
+ }
}
void UserMediaRequest::contextDestroyed()
{
- Ref<UserMediaRequest> protect(*this);
-
+ ContextDestructionObserver::contextDestroyed();
+ Ref<UserMediaRequest> protectedThis(*this);
if (m_controller) {
- m_controller->cancelRequest(this);
- m_controller = 0;
+ m_controller->cancelUserMediaAccessRequest(*this);
+ m_controller = nullptr;
}
+}
- ContextDestructionObserver::contextDestroyed();
+Document* UserMediaRequest::document() const
+{
+ return downcast<Document>(m_scriptExecutionContext);
}
} // namespace WebCore