diff options
Diffstat (limited to 'Source/WebCore/Modules/mediastream/UserMediaRequest.cpp')
-rw-r--r-- | Source/WebCore/Modules/mediastream/UserMediaRequest.cpp | 264 |
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 |