WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Rebased patch.
apply_constraints_patch_10.txt (text/plain), 114.64 KB, created by
Eric Carlson
on 2016-09-01 10:05:21 PDT
(
hide
)
Description:
Rebased patch.
Filename:
MIME Type:
Creator:
Eric Carlson
Created:
2016-09-01 10:05:21 PDT
Size:
114.64 KB
patch
obsolete
>Index: Source/WebCore/ChangeLog >=================================================================== >--- Source/WebCore/ChangeLog (revision 205291) >+++ Source/WebCore/ChangeLog (working copy) >@@ -1,3 +1,132 @@ >+2016-09-01 Eric Carlson <eric.carlson@apple.com> >+ >+ [MediaStream] applyConstraints pt. 1 - mandatory constraints >+ https://bugs.webkit.org/show_bug.cgi?id=161469 >+ <rdar://problem/28109325> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Tests: fast/mediastream/apply-constraints-audio.html >+ fast/mediastream/apply-constraints-video.html >+ >+ * Modules/mediastream/MediaStreamTrack.cpp: >+ (WebCore::MediaStreamTrack::MediaStreamTrack): Initialize the weak pointer factory. >+ (WebCore::MediaStreamTrack::applyConstraints): Make it work. >+ * Modules/mediastream/MediaStreamTrack.h: >+ * Modules/mediastream/MediaStreamTrack.idl: >+ >+ * WebCore.xcodeproj/project.pbxproj: Add JSMediaDevicesCustom.h. >+ >+ * bindings/js/JSMediaDevicesCustom.cpp: >+ (WebCore::createStringConstraint): Add name parameter. >+ (WebCore::createBooleanConstraint): Ditto. >+ (WebCore::createDoubleConstraint): Ditto. >+ (WebCore::createIntConstraint): Ditto. >+ (WebCore::parseMediaTrackConstraintSetForKey): Drop type parameter because we don't need to >+ filter by source media type. >+ (WebCore::parseAdvancedConstraints): Ditto. >+ (WebCore::parseMediaConstraintsDictionary): Renamed from parseConstraints. >+ (WebCore::JSMediaDevices::getUserMedia): Don't throw exceptions, always return a promise. >+ (WebCore::parseConstraints): Deleted. >+ * bindings/js/JSMediaDevicesCustom.h: Added. >+ >+ * bindings/js/JSMediaStreamTrackCustom.cpp: >+ (WebCore::JSMediaStreamTrack::getSettings): Don't include aspect ratio if the value is 0. >+ (WebCore::capabilityValue): asULong -> asInt. >+ (WebCore::JSMediaStreamTrack::applyConstraints): New. >+ (WebCore::JSMediaStreamTrack::getConstraints): New. >+ >+ * bindings/js/WebCoreBuiltinNames.h: Add "mediaStreamTrackConstraints". >+ >+ * html/HTMLMediaElement.cpp: >+ (WebCore::HTMLMediaElement::setSrcObject): Drive by fix: don't call DOMURL::createPublicURL(null). >+ >+ * platform/mediastream/MediaConstraints.cpp: >+ (WebCore::MediaConstraint::create): Pass name to constructors. >+ (WebCore::StringConstraint::find): New. >+ * platform/mediastream/MediaConstraints.h: >+ >+ * platform/mediastream/MediaStreamTrackPrivate.cpp: >+ (WebCore::MediaStreamTrackPrivate::applyConstraints): Add callback parameters. >+ * platform/mediastream/MediaStreamTrackPrivate.h: >+ >+ * platform/mediastream/RealtimeMediaSource.cpp: >+ (WebCore::RealtimeMediaSource::RealtimeMediaSource): Initialize weak pointer factory. >+ (WebCore::RealtimeMediaSource::settingsDidChange): Don't call observers immediately so we can >+ coalesce multiple changes in the same runloop cycle. >+ (WebCore::RealtimeMediaSource::supportsConstraint): New. >+ (WebCore::value): Return the most appropriate value from a numeric constraint. >+ (WebCore::RealtimeMediaSource::applyConstraint): New, apply one constraint. >+ (WebCore::RealtimeMediaSource::applyConstraints): New, validate and apply constraints. >+ (WebCore::RealtimeMediaSource::setWidth): New. >+ (WebCore::RealtimeMediaSource::setHeight): New. >+ (WebCore::RealtimeMediaSource::setFrameRate): New. >+ (WebCore::RealtimeMediaSource::setAspectRatio): New. >+ (WebCore::RealtimeMediaSource::setFacingMode): New. >+ (WebCore::RealtimeMediaSource::setVolume): New. >+ (WebCore::RealtimeMediaSource::setSampleRate): New. >+ (WebCore::RealtimeMediaSource::setSampleSize): New. >+ (WebCore::RealtimeMediaSource::setEchoCancellation) New.: >+ (WebCore::RealtimeMediaSource::scheduleDeferredTask): New. >+ * platform/mediastream/RealtimeMediaSource.h: >+ >+ * platform/mediastream/RealtimeMediaSourceCapabilities.h: >+ (WebCore::CapabilityValueOrRange::CapabilityValueOrRange): "unsigned long" -> "int" >+ >+ * platform/mediastream/RealtimeMediaSourceSettings.cpp: >+ (WebCore::userFacing): New. >+ (WebCore::environmentFacing): New. >+ (WebCore::leftFacing): New. >+ (WebCore::rightFacing): New. >+ (WebCore::RealtimeMediaSourceSettings::facingMode): >+ (WebCore::RealtimeMediaSourceSettings::videoFacingModeEnum): >+ * platform/mediastream/RealtimeMediaSourceSettings.h: >+ >+ * platform/mediastream/mac/AVAudioCaptureSource.mm: >+ (WebCore::AVAudioCaptureSource::initializeCapabilities): Volume range is 0.0 .. 1.0. >+ >+ * platform/mediastream/mac/AVMediaCaptureSource.h: >+ (WebCore::AVMediaCaptureSource::createWeakPtr): Deleted. >+ * platform/mediastream/mac/AVMediaCaptureSource.mm: >+ (WebCore::AVMediaCaptureSource::AVMediaCaptureSource): Don't need the weak ptr factory, it is >+ in the base class. >+ (WebCore::AVMediaCaptureSource::scheduleDeferredTask): Deleted. >+ >+ * platform/mediastream/mac/AVVideoCaptureSource.h: >+ * platform/mediastream/mac/AVVideoCaptureSource.mm: >+ (WebCore::AVVideoCaptureSource::applySize): New. >+ (WebCore::AVVideoCaptureSource::applyFrameRate): New. >+ (WebCore::AVVideoCaptureSource::setupCaptureSession): >+ (WebCore::AVVideoCaptureSource::setFrameRateConstraint): Deleted. >+ (WebCore::AVVideoCaptureSource::applyConstraints): Deleted. >+ >+ * platform/mock/MockRealtimeAudioSource.cpp: >+ (WebCore::MockRealtimeAudioSource::updateSettings): Set volume and echoCancellation to the >+ current values. >+ (WebCore::MockRealtimeAudioSource::initializeCapabilities): Volume takes a float, not an int. >+ * platform/mock/MockRealtimeAudioSource.h: >+ >+ * platform/mock/MockRealtimeMediaSource.cpp: Minor cleanup. >+ * platform/mock/MockRealtimeMediaSource.h: >+ >+ * platform/mock/MockRealtimeVideoSource.cpp: >+ (WebCore::MockRealtimeVideoSource::MockRealtimeVideoSource): Initialize frame rate. >+ (WebCore::MockRealtimeVideoSource::startProducingData): m_size -> size(). >+ (WebCore::MockRealtimeVideoSource::updateSettings): Use accessors because instance variables >+ have been moved to the base class. >+ (WebCore::MockRealtimeVideoSource::initializeCapabilities): Ditto. >+ (WebCore::MockRealtimeVideoSource::applyFrameRate): New. >+ (WebCore::MockRealtimeVideoSource::applySize): >+ (WebCore::MockRealtimeVideoSource::drawAnimation): >+ (WebCore::MockRealtimeVideoSource::drawBoxes): >+ (WebCore::MockRealtimeVideoSource::drawText): >+ (WebCore::MockRealtimeVideoSource::generateFrame): >+ (WebCore::MockRealtimeVideoSource::imageBuffer): >+ (WebCore::MockRealtimeVideoSource::setFrameRate): Deleted. >+ (WebCore::MockRealtimeVideoSource::setSize): Deleted. >+ * platform/mock/MockRealtimeVideoSource.h: >+ (WebCore::MockRealtimeVideoSource::size): Deleted. >+ > 2016-09-01 Fujii Hironori <Hironori.Fujii@sony.com> > > Fix the Mac cmake clean build issue >Index: Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp >=================================================================== >--- Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp (revision 205291) >+++ Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp (working copy) >@@ -35,12 +35,14 @@ > #include "EventNames.h" > #include "ExceptionCode.h" > #include "ExceptionCodePlaceholder.h" >+#include "JSOverconstrainedError.h" > #include "MediaConstraintsImpl.h" > #include "MediaSourceSettings.h" > #include "MediaStream.h" > #include "MediaStreamPrivate.h" > #include "MediaTrackConstraints.h" > #include "NotImplemented.h" >+#include "OverconstrainedError.h" > #include "ScriptExecutionContext.h" > #include <wtf/NeverDestroyed.h> > >@@ -52,9 +54,9 @@ Ref<MediaStreamTrack> MediaStreamTrack:: > } > > MediaStreamTrack::MediaStreamTrack(ScriptExecutionContext& context, MediaStreamTrackPrivate& privateTrack) >- : RefCounted() >- , ActiveDOMObject(&context) >+ : ActiveDOMObject(&context) > , m_private(privateTrack) >+ , m_weakPtrFactory(this) > { > suspendIfNeeded(); > >@@ -167,18 +169,37 @@ RefPtr<RealtimeMediaSourceCapabilities> > return m_private->capabilities(); > } > >-void MediaStreamTrack::applyConstraints(const Dictionary& constraints) >+void MediaStreamTrack::applyConstraints(Ref<MediaConstraints>&& constraints, ApplyConstraintsPromise&& promise) > { >- // FIXME: Implement correctly. https://bugs.webkit.org/show_bug.cgi?id=160579 >+ if (!constraints->isValid()) { >+ promise.reject(TypeError); >+ return; >+ } >+ >+ m_constraints = WTFMove(constraints); >+ m_promise = WTFMove(promise); > >- m_constraints->initialize(constraints); >- m_private->applyConstraints(*m_constraints); >+ applyConstraints(*m_constraints); > } > >-void MediaStreamTrack::applyConstraints(const MediaConstraints&) >+void MediaStreamTrack::applyConstraints(const MediaConstraints& constraints) > { >- // FIXME: apply the new constraints to the track >- // https://bugs.webkit.org/show_bug.cgi?id=122428 >+ auto weakThis = createWeakPtr(); >+ std::function<void(const String&, const String&)> failureHandler = [weakThis](const String& failedConstraint, const String& message) { >+ if (!weakThis || !weakThis->m_promise) >+ return; >+ >+ weakThis->m_promise->reject(OverconstrainedError::create(failedConstraint, message).get()); >+ }; >+ >+ std::function<void()> successHandler = [weakThis]() { >+ if (!weakThis || !weakThis->m_promise) >+ return; >+ >+ weakThis->m_promise->resolve(nullptr); >+ }; >+ >+ m_private->applyConstraints(constraints, successHandler, failureHandler); > } > > void MediaStreamTrack::addObserver(MediaStreamTrack::Observer* observer) >Index: Source/WebCore/Modules/mediastream/MediaStreamTrack.h >=================================================================== >--- Source/WebCore/Modules/mediastream/MediaStreamTrack.h (revision 205291) >+++ Source/WebCore/Modules/mediastream/MediaStreamTrack.h (working copy) >@@ -31,18 +31,20 @@ > > #include "ActiveDOMObject.h" > #include "EventTarget.h" >+#include "JSDOMPromise.h" > #include "MediaStreamTrackPrivate.h" > #include "RealtimeMediaSource.h" > #include "ScriptWrappable.h" >+#include <wtf/Optional.h> > #include <wtf/RefPtr.h> > #include <wtf/Vector.h> >+#include <wtf/WeakPtr.h> > #include <wtf/text/WTFString.h> > > namespace WebCore { > > class AudioSourceProvider; >-class Dictionary; >-class MediaConstraintsImpl; >+class MediaConstraints; > class MediaSourceSettings; > class MediaTrackConstraints; > >@@ -79,7 +81,9 @@ public: > RefPtr<MediaTrackConstraints> getConstraints() const; > RefPtr<MediaSourceSettings> getSettings() const; > RefPtr<RealtimeMediaSourceCapabilities> getCapabilities() const; >- void applyConstraints(const Dictionary&); >+ >+ using ApplyConstraintsPromise = DOMPromise<std::nullptr_t>; >+ void applyConstraints(Ref<MediaConstraints>&&, ApplyConstraintsPromise&&); > void applyConstraints(const MediaConstraints&); > > RealtimeMediaSource& source() const { return m_private->source(); } >@@ -118,10 +122,14 @@ private: > void trackSettingsChanged(MediaStreamTrackPrivate&) override; > void trackEnabledChanged(MediaStreamTrackPrivate&) override; > >+ WeakPtr<MediaStreamTrack> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); } >+ > Vector<Observer*> m_observers; > Ref<MediaStreamTrackPrivate> m_private; > >- RefPtr<MediaConstraintsImpl> m_constraints; >+ RefPtr<MediaConstraints> m_constraints; >+ Optional<ApplyConstraintsPromise> m_promise; >+ WeakPtrFactory<MediaStreamTrack> m_weakPtrFactory; > > bool m_ended { false }; > }; >Index: Source/WebCore/Modules/mediastream/MediaStreamTrack.idl >=================================================================== >--- Source/WebCore/Modules/mediastream/MediaStreamTrack.idl (revision 205291) >+++ Source/WebCore/Modules/mediastream/MediaStreamTrack.idl (working copy) >@@ -47,10 +47,10 @@ enum MediaStreamTrackState { "new", "liv > MediaStreamTrack clone(); > [ImplementedAs=stopProducingData] void stop(); > >- MediaTrackConstraints getConstraints(); >+ [Custom] MediaTrackConstraints getConstraints(); > [Custom] MediaSourceSettings getSettings(); > [Custom] MediaTrackCapabilities getCapabilities(); >- void applyConstraints(Dictionary constraints); >+ [Custom] Promise applyConstraints(Dictionary? constraints); > > attribute EventHandler onoverconstrained; > }; >Index: Source/WebCore/WebCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/WebCore/WebCore.xcodeproj/project.pbxproj (revision 205291) >+++ Source/WebCore/WebCore.xcodeproj/project.pbxproj (working copy) >@@ -139,6 +139,7 @@ > 07297FA71C1881C5003F0735 /* UserMediaPermissionCheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07297FA51C1881C5003F0735 /* UserMediaPermissionCheck.cpp */; }; > 07297FA81C1881C5003F0735 /* UserMediaPermissionCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 07297FA61C1881C5003F0735 /* UserMediaPermissionCheck.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 072A70401D6E8F6200DF0AFC /* OverconstrainedErrorEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 072A703E1D6E8F6200DF0AFC /* OverconstrainedErrorEvent.h */; }; >+ 072A70431D7396B300DF0AFC /* JSMediaDevicesCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 072A70421D7396B200DF0AFC /* JSMediaDevicesCustom.h */; }; > 072AE1E5183C0741000A5988 /* PluginReplacement.h in Headers */ = {isa = PBXBuildFile; fileRef = 072AE1DF183C0741000A5988 /* PluginReplacement.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 072AE1E6183C0741000A5988 /* QuickTimePluginReplacement.mm in Sources */ = {isa = PBXBuildFile; fileRef = 072AE1E0183C0741000A5988 /* QuickTimePluginReplacement.mm */; }; > 072AE1E8183C0741000A5988 /* QuickTimePluginReplacement.h in Headers */ = {isa = PBXBuildFile; fileRef = 072AE1E2183C0741000A5988 /* QuickTimePluginReplacement.h */; }; >@@ -6944,6 +6945,7 @@ > 07297FA61C1881C5003F0735 /* UserMediaPermissionCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserMediaPermissionCheck.h; sourceTree = "<group>"; }; > 072A703E1D6E8F6200DF0AFC /* OverconstrainedErrorEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OverconstrainedErrorEvent.h; sourceTree = "<group>"; }; > 072A703F1D6E8F6200DF0AFC /* OverconstrainedErrorEvent.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OverconstrainedErrorEvent.idl; sourceTree = "<group>"; }; >+ 072A70421D7396B200DF0AFC /* JSMediaDevicesCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMediaDevicesCustom.h; sourceTree = "<group>"; }; > 072AE1DF183C0741000A5988 /* PluginReplacement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginReplacement.h; sourceTree = "<group>"; }; > 072AE1E0183C0741000A5988 /* QuickTimePluginReplacement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QuickTimePluginReplacement.mm; sourceTree = "<group>"; }; > 072AE1E1183C0741000A5988 /* QuickTimePluginReplacement.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = QuickTimePluginReplacement.css; sourceTree = "<group>"; }; >@@ -21098,6 +21100,7 @@ > 7A74ECBC101839DA00BF939E /* JSInspectorFrontendHostCustom.cpp */, > BCE1C43F0D9830F4003B02F2 /* JSLocationCustom.cpp */, > 1B88DD121D5AD3B200E3B7A4 /* JSMediaDevicesCustom.cpp */, >+ 072A70421D7396B200DF0AFC /* JSMediaDevicesCustom.h */, > AD726FE716D9F204003A4E6D /* JSMediaListCustom.h */, > 07C59B6D17F794F6000FBCBB /* JSMediaStreamTrackCustom.cpp */, > 07C1C0E61BFB90A700BD2256 /* JSMediaTrackSupportedConstraintsCustom.cpp */, >@@ -26283,6 +26286,7 @@ > 93309EA3099EB78C0056E581 /* SharedTimer.h in Headers */, > E48944A3180B57D800F165D8 /* SimpleLineLayout.h in Headers */, > 585D6E041A1A792E00FA4F12 /* SimpleLineLayoutFlowContents.h in Headers */, >+ 072A70431D7396B300DF0AFC /* JSMediaDevicesCustom.h in Headers */, > E4E9B11D1814569C003ACCDF /* SimpleLineLayoutFunctions.h in Headers */, > E4E9B1191810916F003ACCDF /* SimpleLineLayoutResolver.h in Headers */, > 582CB0531A78A14B00AFFCC4 /* SimpleLineLayoutTextFragmentIterator.h in Headers */, >Index: Source/WebCore/bindings/js/JSMediaDevicesCustom.cpp >=================================================================== >--- Source/WebCore/bindings/js/JSMediaDevicesCustom.cpp (revision 205291) >+++ Source/WebCore/bindings/js/JSMediaDevicesCustom.cpp (working copy) >@@ -24,15 +24,15 @@ > */ > > #include "config.h" >-#include "JSMediaDevices.h" >+#include "JSMediaDevicesCustom.h" > > #if ENABLE(MEDIA_STREAM) > > #include "ArrayValue.h" > #include "Dictionary.h" > #include "ExceptionCode.h" >+#include "JSMediaDevices.h" > #include "Logging.h" >-#include "MediaConstraints.h" > #include "MediaConstraintsImpl.h" > #include "RealtimeMediaSourceCenter.h" > #include "RealtimeMediaSourceSupportedConstraints.h" >@@ -60,7 +60,7 @@ static void initializeStringConstraintWi > > static RefPtr<StringConstraint> createStringConstraint(const Dictionary& mediaTrackConstraintSet, const String& name, MediaConstraintType type, ConstraintSetType constraintSetType) > { >- auto constraint = StringConstraint::create(type); >+ auto constraint = StringConstraint::create(name, type); > > // Dictionary constraint value. > Dictionary dictionaryValue; >@@ -122,7 +122,7 @@ static RefPtr<StringConstraint> createSt > > static RefPtr<BooleanConstraint> createBooleanConstraint(const Dictionary& mediaTrackConstraintSet, const String& name, MediaConstraintType type, ConstraintSetType constraintSetType) > { >- auto constraint = BooleanConstraint::create(type); >+ auto constraint = BooleanConstraint::create(name, type); > > // Dictionary constraint value. > Dictionary dictionaryValue; >@@ -161,7 +161,7 @@ static RefPtr<BooleanConstraint> createB > > static RefPtr<DoubleConstraint> createDoubleConstraint(const Dictionary& mediaTrackConstraintSet, const String& name, MediaConstraintType type, ConstraintSetType constraintSetType) > { >- auto constraint = DoubleConstraint::create(type); >+ auto constraint = DoubleConstraint::create(name, type); > > // Dictionary constraint value. > Dictionary dictionaryValue; >@@ -208,7 +208,7 @@ static RefPtr<DoubleConstraint> createDo > > static RefPtr<IntConstraint> createIntConstraint(const Dictionary& mediaTrackConstraintSet, const String& name, MediaConstraintType type, ConstraintSetType constraintSetType) > { >- auto constraint = IntConstraint::create(type); >+ auto constraint = IntConstraint::create(name, type); > > // Dictionary constraint value. > Dictionary dictionaryValue; >@@ -253,61 +253,45 @@ static RefPtr<IntConstraint> createIntCo > return nullptr; > } > >-static void parseMediaTrackConstraintSetForKey(const Dictionary& mediaTrackConstraintSet, const String& name, MediaTrackConstraintSetMap& map, ConstraintSetType constraintSetType, RealtimeMediaSource::Type sourceType) >+static void parseMediaTrackConstraintSetForKey(const Dictionary& mediaTrackConstraintSet, const String& name, MediaTrackConstraintSetMap& map, ConstraintSetType constraintSetType) > { > MediaConstraintType constraintType = RealtimeMediaSourceSupportedConstraints::constraintFromName(name); > > RefPtr<MediaConstraint> mediaConstraint; >- if (sourceType == RealtimeMediaSource::Audio) { >- switch (constraintType) { >- case MediaConstraintType::SampleRate: >- case MediaConstraintType::SampleSize: >- mediaConstraint = createIntConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >- break; >- case MediaConstraintType::Volume: >- mediaConstraint = createDoubleConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >- break; >- case MediaConstraintType::EchoCancellation: >- mediaConstraint = createBooleanConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >- break; >- case MediaConstraintType::DeviceId: >- case MediaConstraintType::GroupId: >- mediaConstraint = createStringConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >- break; >- default: >- LOG(Media, "parseMediaTrackConstraintSetForKey() - ignoring unsupported constraint '%s' for audio.", name.utf8().data()); >- mediaConstraint = nullptr; >- break; >- } >- } else if (sourceType == RealtimeMediaSource::Video) { >- switch (constraintType) { >- case MediaConstraintType::Width: >- case MediaConstraintType::Height: >- mediaConstraint = createIntConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >- break; >- case MediaConstraintType::AspectRatio: >- case MediaConstraintType::FrameRate: >- mediaConstraint = createDoubleConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >- break; >- case MediaConstraintType::FacingMode: >- case MediaConstraintType::DeviceId: >- case MediaConstraintType::GroupId: >- mediaConstraint = createStringConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >- break; >- default: >- LOG(Media, "parseMediaTrackConstraintSetForKey() - ignoring unsupported constraint '%s' for video.", name.utf8().data()); >- mediaConstraint = nullptr; >- break; >- } >+ switch (constraintType) { >+ case MediaConstraintType::Width: >+ case MediaConstraintType::Height: >+ case MediaConstraintType::SampleRate: >+ case MediaConstraintType::SampleSize: >+ mediaConstraint = createIntConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >+ break; >+ >+ case MediaConstraintType::AspectRatio: >+ case MediaConstraintType::FrameRate: >+ case MediaConstraintType::Volume: >+ mediaConstraint = createDoubleConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >+ break; >+ >+ case MediaConstraintType::EchoCancellation: >+ mediaConstraint = createBooleanConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >+ break; >+ >+ case MediaConstraintType::FacingMode: >+ case MediaConstraintType::DeviceId: >+ case MediaConstraintType::GroupId: >+ mediaConstraint = createStringConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType); >+ break; >+ >+ case MediaConstraintType::Unknown: >+ LOG(Media, "parseMediaTrackConstraintSetForKey() - found unsupported constraint '%s'.", name.utf8().data()); >+ mediaConstraint = UnknownConstraint::create(name, constraintType); >+ break; > } >- >- if (!mediaConstraint) >- return; >- >+ > map.add(name, WTFMove(mediaConstraint)); > } > >-static void parseAdvancedConstraints(const Dictionary& mediaTrackConstraints, Vector<MediaTrackConstraintSetMap>& advancedConstraints, RealtimeMediaSource::Type sourceType) >+static void parseAdvancedConstraints(const Dictionary& mediaTrackConstraints, Vector<MediaTrackConstraintSetMap>& advancedConstraints) > { > ArrayValue sequenceOfMediaTrackConstraintSets; > if (!mediaTrackConstraints.get("advanced", sequenceOfMediaTrackConstraintSets) || sequenceOfMediaTrackConstraintSets.isUndefinedOrNull()) { >@@ -333,14 +317,14 @@ static void parseAdvancedConstraints(con > Vector<String> localKeys; > mediaTrackConstraintSet.getOwnPropertyNames(localKeys); > for (auto& localKey : localKeys) >- parseMediaTrackConstraintSetForKey(mediaTrackConstraintSet, localKey, map, ConstraintSetType::Advanced, sourceType); >+ parseMediaTrackConstraintSetForKey(mediaTrackConstraintSet, localKey, map, ConstraintSetType::Advanced); > > if (!map.isEmpty()) > advancedConstraints.append(WTFMove(map)); > } > } > >-static void parseConstraints(const Dictionary& mediaTrackConstraints, MediaTrackConstraintSetMap& mandatoryConstraints, Vector<MediaTrackConstraintSetMap>& advancedConstraints, RealtimeMediaSource::Type sourceType) >+void parseMediaConstraintsDictionary(const Dictionary& mediaTrackConstraints, MediaTrackConstraintSetMap& mandatoryConstraints, Vector<MediaTrackConstraintSetMap>& advancedConstraints) > { > if (mediaTrackConstraints.isUndefinedOrNull()) > return; >@@ -350,9 +334,9 @@ static void parseConstraints(const Dicti > > for (auto& key : keys) { > if (key == "advanced") >- parseAdvancedConstraints(mediaTrackConstraints, advancedConstraints, sourceType); >+ parseAdvancedConstraints(mediaTrackConstraints, advancedConstraints); > else >- parseMediaTrackConstraintSetForKey(mediaTrackConstraints, key, mandatoryConstraints, ConstraintSetType::Mandatory, sourceType); >+ parseMediaTrackConstraintSetForKey(mediaTrackConstraints, key, mandatoryConstraints, ConstraintSetType::Mandatory); > } > } > >@@ -361,36 +345,37 @@ JSValue JSMediaDevices::getUserMedia(Exe > VM& vm = state.vm(); > auto scope = DECLARE_THROW_SCOPE(vm); > >- if (UNLIKELY(state.argumentCount() < 1)) >- return JSValue::decode(throwVMError(&state, scope, createNotEnoughArgumentsError(&state))); >- ExceptionCode ec = 0; >- auto constraintsDictionary = Dictionary(&state, state.uncheckedArgument(0)); >+ MediaTrackConstraintSetMap mandatoryVideoConstraints; >+ Vector<MediaTrackConstraintSetMap> advancedVideoConstraints; >+ bool areVideoConstraintsValid = false; > > MediaTrackConstraintSetMap mandatoryAudioConstraints; > Vector<MediaTrackConstraintSetMap> advancedAudioConstraints; > bool areAudioConstraintsValid = false; > >- Dictionary audioConstraintsDictionary; >- if (constraintsDictionary.get("audio", audioConstraintsDictionary) && !audioConstraintsDictionary.isUndefinedOrNull()) { >- parseConstraints(audioConstraintsDictionary, mandatoryAudioConstraints, advancedAudioConstraints, RealtimeMediaSource::Audio); >- areAudioConstraintsValid = true; >- } else >- constraintsDictionary.get("audio", areAudioConstraintsValid); >+ if (state.argumentCount() >= 1) { >+ auto constraintsDictionary = Dictionary(&state, state.uncheckedArgument(0)); > >- MediaTrackConstraintSetMap mandatoryVideoConstraints; >- Vector<MediaTrackConstraintSetMap> advancedVideoConstraints; >- bool areVideoConstraintsValid = false; >- >- Dictionary videoConstraintsDictionary; >- if (constraintsDictionary.get("video", videoConstraintsDictionary) && !videoConstraintsDictionary.isUndefinedOrNull()) { >- parseConstraints(videoConstraintsDictionary, mandatoryVideoConstraints, advancedVideoConstraints, RealtimeMediaSource::Video); >- areVideoConstraintsValid = true; >- } else >- constraintsDictionary.get("video", areVideoConstraintsValid); >+ Dictionary audioConstraintsDictionary; >+ if (constraintsDictionary.get("audio", audioConstraintsDictionary) && !audioConstraintsDictionary.isUndefinedOrNull()) { >+ parseMediaConstraintsDictionary(audioConstraintsDictionary, mandatoryAudioConstraints, advancedAudioConstraints); >+ areAudioConstraintsValid = true; >+ } else >+ constraintsDictionary.get("audio", areAudioConstraintsValid); >+ >+ Dictionary videoConstraintsDictionary; >+ if (constraintsDictionary.get("video", videoConstraintsDictionary) && !videoConstraintsDictionary.isUndefinedOrNull()) { >+ parseMediaConstraintsDictionary(videoConstraintsDictionary, mandatoryVideoConstraints, advancedVideoConstraints); >+ areVideoConstraintsValid = true; >+ } else >+ constraintsDictionary.get("video", areVideoConstraintsValid); >+ } > > auto audioConstraints = MediaConstraintsImpl::create(WTFMove(mandatoryAudioConstraints), WTFMove(advancedAudioConstraints), areAudioConstraintsValid); > auto videoConstraints = MediaConstraintsImpl::create(WTFMove(mandatoryVideoConstraints), WTFMove(advancedVideoConstraints), areVideoConstraintsValid); > JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, globalObject()); >+ >+ ExceptionCode ec = 0; > wrapped().getUserMedia(WTFMove(audioConstraints), WTFMove(videoConstraints), DeferredWrapper::create(&state, globalObject(), promiseDeferred), ec); > setDOMException(&state, ec); > return promiseDeferred->promise(); >Index: Source/WebCore/bindings/js/JSMediaDevicesCustom.h >=================================================================== >--- Source/WebCore/bindings/js/JSMediaDevicesCustom.h (nonexistent) >+++ Source/WebCore/bindings/js/JSMediaDevicesCustom.h (working copy) >@@ -0,0 +1,41 @@ >+/* >+ * Copyright (C) 2016 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#if ENABLE(MEDIA_STREAM) >+ >+#include "MediaConstraints.h" >+#include <wtf/Vector.h> >+ >+namespace WebCore { >+ >+class Dictionary; >+ >+void parseMediaConstraintsDictionary(const Dictionary&, MediaTrackConstraintSetMap&, Vector<MediaTrackConstraintSetMap>&); >+ >+} >+ >+#endif >Index: Source/WebCore/bindings/js/JSMediaStreamTrackCustom.cpp >=================================================================== >--- Source/WebCore/bindings/js/JSMediaStreamTrackCustom.cpp (revision 205291) >+++ Source/WebCore/bindings/js/JSMediaStreamTrackCustom.cpp (working copy) >@@ -28,10 +28,14 @@ > > #if ENABLE(MEDIA_STREAM) > >+#include "Dictionary.h" > #include "ExceptionCode.h" > #include "JSDOMBinding.h" >+#include "JSMediaDevicesCustom.h" >+#include "MediaConstraintsImpl.h" > #include "MediaSourceSettings.h" > #include "MediaStreamTrack.h" >+#include "WebCoreJSClientData.h" > #include <runtime/JSObject.h> > #include <runtime/ObjectConstructor.h> > >@@ -57,7 +61,7 @@ JSC::JSValue JSMediaStreamTrack::getSett > object->putDirect(state.vm(), Identifier::fromString(&state, "width"), jsNumber(settings->width()), DontDelete | ReadOnly); > if (settings->supportsHeight()) > object->putDirect(state.vm(), Identifier::fromString(&state, "height"), jsNumber(settings->height()), DontDelete | ReadOnly); >- if (settings->supportsAspectRatio()) >+ if (settings->supportsAspectRatio() && settings->aspectRatio()) > object->putDirect(state.vm(), Identifier::fromString(&state, "aspectRatio"), jsDoubleNumber(settings->aspectRatio()), DontDelete | ReadOnly); > if (settings->supportsFrameRate()) > object->putDirect(state.vm(), Identifier::fromString(&state, "frameRate"), jsDoubleNumber(settings->frameRate()), DontDelete | ReadOnly); >@@ -90,8 +94,8 @@ static JSValue capabilityValue(const Cap > object->putDirect(state.vm(), Identifier::fromString(&state, "min"), jsNumber(min.asDouble)); > object->putDirect(state.vm(), Identifier::fromString(&state, "max"), jsNumber(max.asDouble)); > } else { >- object->putDirect(state.vm(), Identifier::fromString(&state, "min"), jsNumber(min.asULong)); >- object->putDirect(state.vm(), Identifier::fromString(&state, "max"), jsNumber(max.asULong)); >+ object->putDirect(state.vm(), Identifier::fromString(&state, "min"), jsNumber(min.asInt)); >+ object->putDirect(state.vm(), Identifier::fromString(&state, "max"), jsNumber(max.asInt)); > } > > return object; >@@ -100,7 +104,7 @@ static JSValue capabilityValue(const Cap > if (value.type() == CapabilityValueOrRange::Double) > return jsNumber(value.value().asDouble); > >- return jsNumber(value.value().asULong); >+ return jsNumber(value.value().asInt); > } > > JSC::JSValue JSMediaStreamTrack::getCapabilities(ExecState& state) >@@ -162,6 +166,38 @@ JSC::JSValue JSMediaStreamTrack::getCapa > return object; > } > >+JSValue JSMediaStreamTrack::applyConstraints(ExecState& state) >+{ >+ MediaTrackConstraintSetMap mandatoryConstraints; >+ Vector<MediaTrackConstraintSetMap> advancedConstraints; >+ bool valid = false; >+ >+ if (state.argumentCount() >= 1) { >+ JSValue argument = state.uncheckedArgument(0); >+ >+ JSVMClientData& clientData = *static_cast<JSVMClientData*>(state.vm().clientData); >+ putDirect(state.vm(), clientData.builtinNames().mediaStreamTrackConstraintsPrivateName(), argument, DontEnum); >+ >+ auto constraintsDictionary = Dictionary(&state, argument); >+ if (!constraintsDictionary.isUndefinedOrNull()) >+ parseMediaConstraintsDictionary(constraintsDictionary, mandatoryConstraints, advancedConstraints); >+ valid = !advancedConstraints.isEmpty() || !mandatoryConstraints.isEmpty(); >+ } >+ >+ JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, globalObject()); >+ auto constraints = MediaConstraintsImpl::create(WTFMove(mandatoryConstraints), WTFMove(advancedConstraints), valid); >+ wrapped().applyConstraints(WTFMove(constraints), DeferredWrapper(&state, globalObject(), promiseDeferred)); >+ >+ return promiseDeferred->promise(); >+} >+ >+JSValue JSMediaStreamTrack::getConstraints(ExecState& state) >+{ >+ JSVMClientData& clientData = *static_cast<JSVMClientData*>(state.vm().clientData); >+ JSValue result = getDirect(state.vm(), clientData.builtinNames().mediaStreamTrackConstraintsPrivateName()); >+ return !result.isEmpty() ? result : jsUndefined(); >+} >+ > } // namespace WebCore > > #endif >Index: Source/WebCore/bindings/js/WebCoreBuiltinNames.h >=================================================================== >--- Source/WebCore/bindings/js/WebCoreBuiltinNames.h (revision 205291) >+++ Source/WebCore/bindings/js/WebCoreBuiltinNames.h (working copy) >@@ -58,6 +58,7 @@ namespace WebCore { > macro(localStreams) \ > macro(makeThisTypeError) \ > macro(makeGetterTypeError) \ >+ macro(mediaStreamTrackConstraints) \ > macro(operations) \ > macro(ownerReadableStream) \ > macro(privateGetStats) \ >Index: Source/WebCore/html/HTMLMediaElement.cpp >=================================================================== >--- Source/WebCore/html/HTMLMediaElement.cpp (revision 205291) >+++ Source/WebCore/html/HTMLMediaElement.cpp (working copy) >@@ -1001,7 +1001,8 @@ void HTMLMediaElement::setSrcObject(Scri > // https://bugs.webkit.org/show_bug.cgi?id=124896 > > m_mediaStreamSrcObject = mediaStream; >- setSrc(DOMURL::createPublicURL(context, mediaStream)); >+ if (mediaStream) >+ setSrc(DOMURL::createPublicURL(context, mediaStream)); > } > #endif > >Index: Source/WebCore/platform/mediastream/MediaConstraints.cpp >=================================================================== >--- Source/WebCore/platform/mediastream/MediaConstraints.cpp (revision 205291) >+++ Source/WebCore/platform/mediastream/MediaConstraints.cpp (working copy) >@@ -46,19 +46,19 @@ RefPtr<MediaConstraint> MediaConstraint: > case MediaConstraintType::Height: > case MediaConstraintType::SampleRate: > case MediaConstraintType::SampleSize: >- return IntConstraint::create(constraintType); >+ return IntConstraint::create(name, constraintType); > case MediaConstraintType::AspectRatio: > case MediaConstraintType::FrameRate: > case MediaConstraintType::Volume: >- return DoubleConstraint::create(constraintType); >+ return DoubleConstraint::create(name, constraintType); > case MediaConstraintType::EchoCancellation: >- return BooleanConstraint::create(constraintType); >+ return BooleanConstraint::create(name, constraintType); > case MediaConstraintType::FacingMode: > case MediaConstraintType::DeviceId: > case MediaConstraintType::GroupId: >- return StringConstraint::create(constraintType); >+ return StringConstraint::create(name, constraintType); > case MediaConstraintType::Unknown: >- return nullptr; >+ return UnknownConstraint::create(name, constraintType); > } > } > >@@ -121,6 +121,21 @@ bool StringConstraint::getIdeal(Vector<S > return true; > } > >+const String& StringConstraint::find(std::function<bool(ConstraintType, const String&)> filter) const >+{ >+ for (auto& constraint : m_exact) { >+ if (filter(ConstraintType::ExactConstraint, constraint)) >+ return constraint; >+ } >+ >+ for (auto& constraint : m_ideal) { >+ if (filter(ConstraintType::IdealConstraint, constraint)) >+ return constraint; >+ } >+ >+ return emptyString(); >+} >+ > } > > #endif // ENABLE(MEDIA_STREAM) >Index: Source/WebCore/platform/mediastream/MediaConstraints.h >=================================================================== >--- Source/WebCore/platform/mediastream/MediaConstraints.h (revision 205291) >+++ Source/WebCore/platform/mediastream/MediaConstraints.h (working copy) >@@ -45,6 +45,8 @@ class MediaConstraint : public RefCounte > public: > static RefPtr<MediaConstraint> create(const String& name); > >+ enum class ConstraintType { ExactConstraint, IdealConstraint, MinConstraint, MaxConstraint }; >+ > virtual ~MediaConstraint() { }; > virtual bool isEmpty() const = 0; > virtual bool isMandatory() const = 0; >@@ -53,11 +55,15 @@ public: > virtual bool getMax(int&) const { ASSERT_NOT_REACHED(); return false; } > virtual bool getExact(int&) const { ASSERT_NOT_REACHED(); return false; } > virtual bool getIdeal(int&) const { ASSERT_NOT_REACHED(); return false; } >+ virtual bool validForRange(int, int) const { ASSERT_NOT_REACHED(); return false; } >+ virtual int find(std::function<bool(ConstraintType, int)>) const { ASSERT_NOT_REACHED(); return 0; } > > virtual bool getMin(double&) const { ASSERT_NOT_REACHED(); return false; } > virtual bool getMax(double&) const { ASSERT_NOT_REACHED(); return false; } > virtual bool getExact(double&) const { ASSERT_NOT_REACHED(); return false; } > virtual bool getIdeal(double&) const { ASSERT_NOT_REACHED(); return false; } >+ virtual bool validForRange(double, double) const { ASSERT_NOT_REACHED(); return false; } >+ virtual double find(std::function<bool(ConstraintType, double)>) const { ASSERT_NOT_REACHED(); return 0; } > > virtual bool getMin(bool&) const { ASSERT_NOT_REACHED(); return false; } > virtual bool getMax(bool&) const { ASSERT_NOT_REACHED(); return false; } >@@ -68,16 +74,20 @@ public: > virtual bool getMax(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; } > virtual bool getExact(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; } > virtual bool getIdeal(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; } >+ virtual const String& find(std::function<bool(ConstraintType, const String&)>) const { ASSERT_NOT_REACHED(); return emptyString(); } > > MediaConstraintType type() const { return m_type; } >+ const String& name() const { return m_name; } > > protected: >- explicit MediaConstraint(MediaConstraintType type) >- : m_type(type) >+ explicit MediaConstraint(const String& name, MediaConstraintType type) >+ : m_name(name) >+ , m_type(type) > { > } > > private: >+ String m_name; > MediaConstraintType m_type; > }; > >@@ -124,9 +134,42 @@ public: > return true; > } > >+ bool validForRange(ValueType rangeMin, ValueType rangeMax) const final { >+ if (isEmpty()) >+ return false; >+ >+ if (m_exact && (m_exact.value() < rangeMin || m_exact.value() > rangeMax)) >+ return false; >+ >+ if (m_min && m_min.value() > rangeMax) >+ return false; >+ >+ if (m_max && m_max.value() < rangeMin) >+ return false; >+ >+ return true; >+ } >+ >+ ValueType find(std::function<bool(ConstraintType, ValueType)> function) const final { >+ if (m_min && function(ConstraintType::MinConstraint, m_min.value())) >+ return m_min.value(); >+ >+ if (m_max && function(ConstraintType::MaxConstraint, m_max.value())) >+ return m_max.value(); >+ >+ if (m_exact && function(ConstraintType::ExactConstraint, m_exact.value())) >+ return m_exact.value(); >+ >+ if (m_ideal && function(ConstraintType::IdealConstraint, m_ideal.value())) >+ return m_ideal.value(); >+ >+ return 0; >+ } >+ >+ > protected: >- explicit NumericConstraint(MediaConstraintType type) >- : MediaConstraint(type) >+ explicit NumericConstraint(const String& name, MediaConstraintType type) >+ : MediaConstraint(name, type) > { > } > >@@ -139,29 +182,29 @@ private: > > class IntConstraint final : public NumericConstraint<int> { > public: >- static Ref<IntConstraint> create(MediaConstraintType type) { return adoptRef(*new IntConstraint(type)); } >+ static Ref<IntConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new IntConstraint(name, type)); } > > private: >- explicit IntConstraint(MediaConstraintType type) >- : NumericConstraint<int>(type) >+ explicit IntConstraint(const String& name, MediaConstraintType type) >+ : NumericConstraint<int>(name, type) > { > } > }; > > class DoubleConstraint final : public NumericConstraint<double> { > public: >- static Ref<DoubleConstraint> create(MediaConstraintType type) { return adoptRef(*new DoubleConstraint(type)); } >+ static Ref<DoubleConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new DoubleConstraint(name, type)); } > > private: >- explicit DoubleConstraint(MediaConstraintType type) >- : NumericConstraint<double>(type) >+ explicit DoubleConstraint(const String& name, MediaConstraintType type) >+ : NumericConstraint<double>(name, type) > { > } > }; > > class BooleanConstraint final : public MediaConstraint { > public: >- static Ref<BooleanConstraint> create(MediaConstraintType type) { return adoptRef(*new BooleanConstraint(type)); } >+ static Ref<BooleanConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new BooleanConstraint(name, type)); } > > void setExact(bool value) { m_exact = value; } > void setIdeal(bool value) { m_ideal = value; } >@@ -173,18 +216,18 @@ public: > bool isMandatory() const final { return bool(m_exact); } > > private: >- explicit BooleanConstraint(MediaConstraintType type) >- : MediaConstraint(type) >+ explicit BooleanConstraint(const String& name, MediaConstraintType type) >+ : MediaConstraint(name, type) > { > } > >- Optional<bool> m_exact { false }; >- Optional<bool> m_ideal { false }; >+ Optional<bool> m_exact; >+ Optional<bool> m_ideal; > }; > > class StringConstraint final : public MediaConstraint { > public: >- static Ref<StringConstraint> create(MediaConstraintType type) { return adoptRef(*new StringConstraint(type)); } >+ static Ref<StringConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new StringConstraint(name, type)); } > > void setExact(const String&); > void appendExact(const String&); >@@ -197,9 +240,11 @@ public: > bool isEmpty() const final { return m_exact.isEmpty() && m_ideal.isEmpty(); } > bool isMandatory() const final { return !m_exact.isEmpty(); } > >+ const String& find(std::function<bool(ConstraintType, const String&)>) const override; >+ > private: >- explicit StringConstraint(MediaConstraintType type) >- : MediaConstraint(type) >+ explicit StringConstraint(const String& name, MediaConstraintType type) >+ : MediaConstraint(name, type) > { > } > >@@ -207,6 +252,20 @@ private: > Vector<String> m_ideal; > }; > >+class UnknownConstraint final : public MediaConstraint { >+public: >+ static Ref<UnknownConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new UnknownConstraint(name, type)); } >+ >+ bool isEmpty() const final { return true; } >+ bool isMandatory() const final { return false; } >+ >+private: >+ explicit UnknownConstraint(const String& name, MediaConstraintType type) >+ : MediaConstraint(name, type) >+ { >+ } >+}; >+ > using MediaTrackConstraintSetMap = HashMap<String, RefPtr<MediaConstraint>>; > > class MediaConstraints : public RefCounted<MediaConstraints> { >Index: Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp >=================================================================== >--- Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp (revision 205291) >+++ Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp (working copy) >@@ -172,10 +172,9 @@ void MediaStreamTrackPrivate::paintCurre > } > } > >-void MediaStreamTrackPrivate::applyConstraints(const MediaConstraints&) >+void MediaStreamTrackPrivate::applyConstraints(const MediaConstraints& constraints, RealtimeMediaSource::SuccessHandler successHandler, RealtimeMediaSource::FailureHandler failureHandler) > { >- // FIXME: apply the new constraints to the track >- // https://bugs.webkit.org/show_bug.cgi?id=122428 >+ m_source->applyConstraints(constraints, successHandler, failureHandler); > } > > AudioSourceProvider* MediaStreamTrackPrivate::audioSourceProvider() >Index: Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h >=================================================================== >--- Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h (revision 205291) >+++ Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h (working copy) >@@ -91,7 +91,7 @@ public: > RefPtr<RealtimeMediaSourceCapabilities> capabilities() const; > > RefPtr<MediaConstraints> constraints() const; >- void applyConstraints(const MediaConstraints&); >+ void applyConstraints(const MediaConstraints&, RealtimeMediaSource::SuccessHandler, RealtimeMediaSource::FailureHandler); > > AudioSourceProvider* audioSourceProvider(); > >Index: Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp >=================================================================== >--- Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp (revision 205291) >+++ Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp (working copy) >@@ -36,13 +36,17 @@ > #if ENABLE(MEDIA_STREAM) > #include "RealtimeMediaSource.h" > >+#include "MediaConstraints.h" >+#include "NotImplemented.h" > #include "RealtimeMediaSourceCapabilities.h" > #include "UUID.h" >+#include <wtf/MainThread.h> > > namespace WebCore { > > RealtimeMediaSource::RealtimeMediaSource(const String& id, Type type, const String& name) >- : m_id(id) >+ : m_weakPtrFactory(this) >+ , m_id(id) > , m_type(type) > , m_name(name) > { >@@ -51,6 +55,7 @@ RealtimeMediaSource::RealtimeMediaSource > if (m_id.isEmpty()) > m_id = createCanonicalUUIDString(); > m_persistentID = m_id; >+ m_suppressNotifications = false; > } > > void RealtimeMediaSource::reset() >@@ -92,8 +97,23 @@ void RealtimeMediaSource::setMuted(bool > > void RealtimeMediaSource::settingsDidChange() > { >- for (auto& observer : m_observers) >- observer->sourceSettingsChanged(); >+ { >+ LockHolder lock(m_lock); >+ if (m_pendingSettingsDidChangeNotification || m_suppressNotifications) >+ return; >+ >+ m_pendingSettingsDidChangeNotification = true; >+ } >+ >+ scheduleDeferredTask([this] { >+ { >+ LockHolder lock(m_lock); >+ m_pendingSettingsDidChangeNotification = false; >+ } >+ >+ for (auto& observer : m_observers) >+ observer->sourceSettingsChanged(); >+ }); > } > > void RealtimeMediaSource::mediaDataUpdated(MediaSample& mediaSample) >@@ -134,6 +154,418 @@ void RealtimeMediaSource::requestStop(Ob > stop(callingObserver); > } > >+RealtimeMediaSource::ConstraintSupport RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint) >+{ >+ RealtimeMediaSourceCapabilities& capabilities = *this->capabilities(); >+ >+ switch (constraint.type()) { >+ case MediaConstraintType::Width: { >+ if (!capabilities.supportsWidth()) >+ return ConstraintSupport::Ignored; >+ >+ auto widthRange = capabilities.width(); >+ return constraint.validForRange(widthRange.rangeMin().asInt, widthRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ break; >+ } >+ >+ case MediaConstraintType::Height: { >+ if (!capabilities.supportsHeight()) >+ return ConstraintSupport::Ignored; >+ >+ auto heightRange = capabilities.height(); >+ return constraint.validForRange(heightRange.rangeMin().asInt, heightRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ break; >+ } >+ >+ case MediaConstraintType::FrameRate: { >+ if (!capabilities.supportsFrameRate()) >+ return ConstraintSupport::Ignored; >+ >+ auto rateRange = capabilities.frameRate(); >+ return constraint.validForRange(rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ break; >+ } >+ >+ case MediaConstraintType::AspectRatio: { >+ if (!capabilities.supportsAspectRatio()) >+ return ConstraintSupport::Ignored; >+ >+ auto range = capabilities.aspectRatio(); >+ return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ break; >+ } >+ >+ case MediaConstraintType::Volume: { >+ if (!capabilities.supportsVolume()) >+ return ConstraintSupport::Ignored; >+ >+ auto range = capabilities.volume(); >+ return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ break; >+ } >+ >+ case MediaConstraintType::SampleRate: { >+ if (!capabilities.supportsSampleRate()) >+ return ConstraintSupport::Ignored; >+ >+ auto range = capabilities.sampleRate(); >+ return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ break; >+ } >+ >+ case MediaConstraintType::SampleSize: { >+ if (!capabilities.supportsSampleSize()) >+ return ConstraintSupport::Ignored; >+ >+ auto range = capabilities.sampleSize(); >+ return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ break; >+ } >+ >+ case MediaConstraintType::FacingMode: { >+ if (!capabilities.supportsFacingMode()) >+ return ConstraintSupport::Ignored; >+ >+ ConstraintSupport support = ConstraintSupport::Ignored; >+ auto& supportedModes = capabilities.facingMode(); >+ std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [supportedModes, &support](MediaConstraint::ConstraintType type, const String& modeString) { >+ if (type == MediaConstraint::ConstraintType::ExactConstraint) >+ support = ConstraintSupport::Unsupported; >+ >+ auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString); >+ for (auto& supportedMode : supportedModes) { >+ if (supportedMode == mode) { >+ support = ConstraintSupport::Supported; >+ break; >+ } >+ } >+ >+ return type == MediaConstraint::ConstraintType::ExactConstraint ? true : false; >+ }; >+ >+ constraint.find(filter); >+ return support; >+ break; >+ } >+ >+ case MediaConstraintType::EchoCancellation: >+ if (!capabilities.supportsEchoCancellation()) >+ return ConstraintSupport::Ignored; >+ >+ if (capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadOnly) >+ return constraint.isMandatory() ? ConstraintSupport::Unsupported : ConstraintSupport::Ignored; >+ >+ return ConstraintSupport::Supported; >+ break; >+ >+ case MediaConstraintType::DeviceId: { >+ if (!capabilities.supportsDeviceId()) >+ return ConstraintSupport::Ignored; >+ >+ ConstraintSupport support = ConstraintSupport::Ignored; >+ std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [this, &support](MediaConstraint::ConstraintType type, const String& idString) { >+ if (type != MediaConstraint::ConstraintType::ExactConstraint) >+ return false; // Keep looking. >+ >+ support = idString == m_id ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ return false; >+ }; >+ >+ constraint.find(filter); >+ return support; >+ break; >+ } >+ >+ case MediaConstraintType::GroupId: { >+ if (!capabilities.supportsDeviceId()) >+ return ConstraintSupport::Ignored; >+ >+ ConstraintSupport support = ConstraintSupport::Ignored; >+ String groupId = settings().groupId(); >+ std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [groupId, &support](MediaConstraint::ConstraintType type, const String& idString) { >+ if (type != MediaConstraint::ConstraintType::ExactConstraint) >+ return false; // Keep looking. >+ >+ support = idString == groupId ? ConstraintSupport::Supported : ConstraintSupport::Unsupported; >+ return false; >+ }; >+ >+ constraint.find(filter); >+ return support; >+ break; >+ } >+ >+ case MediaConstraintType::Unknown: >+ // Unknown (or unsupported) constraints should be ignored. >+ break; >+ } >+ >+ return ConstraintSupport::Ignored; >+} >+ >+ >+template <typename T> >+T value(const MediaConstraint& constraint, T rangeMin, T rangeMax) >+{ >+ T result; >+ >+ if (constraint.getExact(result)) { >+ ASSERT(result >= rangeMin && result <= rangeMax); >+ return result; >+ } >+ >+ if (constraint.getIdeal(result)) { >+ if (result < rangeMin) >+ result = rangeMin; >+ else if (result > rangeMax) >+ result = rangeMax; >+ >+ return result; >+ } >+ >+ if (constraint.getMin(result) && result > rangeMax) >+ return false; >+ >+ if (constraint.getMax(result) && result < rangeMin) >+ return false; >+ >+ return result; >+} >+ >+ >+void RealtimeMediaSource::applyConstraint(const MediaConstraint& constraint) >+{ >+ RealtimeMediaSourceCapabilities& capabilities = *this->capabilities(); >+ switch (constraint.type()) { >+ case MediaConstraintType::Width: { >+ if (!capabilities.supportsWidth()) >+ return; >+ >+ auto widthRange = capabilities.width(); >+ setWidth(value(constraint, widthRange.rangeMin().asInt, widthRange.rangeMax().asInt)); >+ break; >+ } >+ >+ case MediaConstraintType::Height: { >+ if (!capabilities.supportsHeight()) >+ return; >+ >+ auto heightRange = capabilities.height(); >+ setHeight(value(constraint, heightRange.rangeMin().asInt, heightRange.rangeMax().asInt)); >+ break; >+ } >+ >+ case MediaConstraintType::FrameRate: { >+ if (!capabilities.supportsFrameRate()) >+ return; >+ >+ auto rateRange = capabilities.frameRate(); >+ setFrameRate(value(constraint, rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble)); >+ break; >+ } >+ >+ case MediaConstraintType::AspectRatio: { >+ if (!capabilities.supportsAspectRatio()) >+ return; >+ >+ auto range = capabilities.aspectRatio(); >+ setAspectRatio(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble)); >+ break; >+ } >+ >+ case MediaConstraintType::Volume: { >+ if (!capabilities.supportsVolume()) >+ return; >+ >+ auto range = capabilities.volume(); >+ // std::pair<T, T> valuesForRange(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble) >+ setVolume(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble)); >+ break; >+ } >+ >+ case MediaConstraintType::SampleRate: { >+ if (!capabilities.supportsSampleRate()) >+ return; >+ >+ auto range = capabilities.sampleRate(); >+ setSampleRate(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble)); >+ break; >+ } >+ >+ case MediaConstraintType::SampleSize: { >+ if (!capabilities.supportsSampleSize()) >+ return; >+ >+ auto range = capabilities.sampleSize(); >+ setSampleSize(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble)); >+ break; >+ } >+ >+ case MediaConstraintType::EchoCancellation: >+ if (!capabilities.supportsEchoCancellation()) >+ return; >+ >+ bool setting; >+ if (constraint.getExact(setting) || constraint.getIdeal(setting)) >+ setEchoCancellation(setting); >+ break; >+ >+ case MediaConstraintType::FacingMode: { >+ if (!capabilities.supportsFacingMode()) >+ return; >+ >+ auto& supportedModes = capabilities.facingMode(); >+ std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [supportedModes](MediaConstraint::ConstraintType, const String& modeString) { >+ auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString); >+ for (auto& supportedMode : supportedModes) { >+ if (mode == supportedMode) >+ return true; >+ } >+ return false; >+ }; >+ >+ auto modeString = constraint.find(filter); >+ if (!modeString.isEmpty()) >+ setFacingMode(RealtimeMediaSourceSettings::videoFacingModeEnum(modeString)); >+ break; >+ } >+ >+ case MediaConstraintType::DeviceId: >+ case MediaConstraintType::GroupId: >+ // There is nothing to do here, neither can be changed. >+ break; >+ >+ case MediaConstraintType::Unknown: >+ break; >+ } >+} >+ >+void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler successHandler, FailureHandler failureHandler) >+{ >+ ASSERT(constraints.isValid()); >+ >+ auto& mandatoryConstraints = constraints.mandatoryConstraints(); >+ for (auto& nameConstraintPair : mandatoryConstraints) { >+ auto& constraint = *nameConstraintPair.value; >+ if (supportsConstraint(constraint) == ConstraintSupport::Unsupported) { >+ failureHandler(constraint.name(), "Constraint not supported"); >+ return; >+ } >+ } >+ >+ for (auto& nameConstraintPair : mandatoryConstraints) >+ applyConstraint(*nameConstraintPair.value); >+ >+ successHandler(); >+} >+ >+void RealtimeMediaSource::setWidth(int width) >+{ >+ if (width == m_size.width()) >+ return; >+ >+ int height = m_aspectRatio ? width / m_aspectRatio : m_size.height(); >+ if (!applySize(IntSize(width, height))) >+ return; >+ >+ m_size.setWidth(width); >+ if (m_aspectRatio) >+ m_size.setHeight(width / m_aspectRatio); >+ >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::setHeight(int height) >+{ >+ if (height == m_size.height()) >+ return; >+ >+ int width = m_aspectRatio ? height * m_aspectRatio : m_size.width(); >+ if (!applySize(IntSize(width, height))) >+ return; >+ >+ if (m_aspectRatio) >+ m_size.setWidth(width); >+ m_size.setHeight(height); >+ >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::setFrameRate(double rate) >+{ >+ if (m_frameRate == rate || !applyFrameRate(rate)) >+ return; >+ >+ m_frameRate = rate; >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::setAspectRatio(double ratio) >+{ >+ if (m_aspectRatio == ratio || !applyAspectRatio(ratio)) >+ return; >+ >+ m_aspectRatio = ratio; >+ m_size.setHeight(m_size.width() / ratio); >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::setFacingMode(RealtimeMediaSourceSettings::VideoFacingMode mode) >+{ >+ if (m_facingMode == mode || !applyFacingMode(mode)) >+ return; >+ >+ m_facingMode = mode; >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::setVolume(double volume) >+{ >+ if (m_volume == volume || !applyVolume(volume)) >+ return; >+ >+ m_volume = volume; >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::setSampleRate(double rate) >+{ >+ if (m_sampleRate == rate || !applySampleRate(rate)) >+ return; >+ >+ m_sampleRate = rate; >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::setSampleSize(double size) >+{ >+ if (m_sampleSize == size || !applySampleSize(size)) >+ return; >+ >+ m_sampleSize = size; >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::setEchoCancellation(bool echoCancellation) >+{ >+ if (m_echoCancellation == echoCancellation || !applyEchoCancellation(echoCancellation)) >+ return; >+ >+ m_echoCancellation = echoCancellation; >+ settingsDidChange(); >+} >+ >+void RealtimeMediaSource::scheduleDeferredTask(std::function<void()>&& function) >+{ >+ ASSERT(function); >+ callOnMainThread([weakThis = createWeakPtr(), function = WTFMove(function)] { >+ if (!weakThis) >+ return; >+ >+ function(); >+ }); >+} >+ > } // namespace WebCore > > #endif // ENABLE(MEDIA_STREAM) >Index: Source/WebCore/platform/mediastream/RealtimeMediaSource.h >=================================================================== >--- Source/WebCore/platform/mediastream/RealtimeMediaSource.h (revision 205291) >+++ Source/WebCore/platform/mediastream/RealtimeMediaSource.h (working copy) >@@ -42,15 +42,16 @@ > #include "MediaSample.h" > #include "PlatformLayer.h" > #include "RealtimeMediaSourceCapabilities.h" >+#include <wtf/Lock.h> > #include <wtf/RefCounted.h> > #include <wtf/Vector.h> >+#include <wtf/WeakPtr.h> > #include <wtf/text/WTFString.h> > > namespace WebCore { > > class FloatRect; > class GraphicsContext; >-class MediaConstraints; > class MediaStreamPrivate; > class RealtimeMediaSourceSettings; > >@@ -90,7 +91,12 @@ public: > > virtual RefPtr<RealtimeMediaSourceCapabilities> capabilities() = 0; > virtual const RealtimeMediaSourceSettings& settings() = 0; >- void settingsDidChange(); >+ >+ using SuccessHandler = std::function<void()>; >+ using FailureHandler = std::function<void(const String& badConstraint, const String& errorString)>; >+ void applyConstraints(const MediaConstraints&, SuccessHandler, FailureHandler); >+ >+ virtual void settingsDidChange(); > void mediaDataUpdated(MediaSample&); > > bool stopped() const { return m_stopped; } >@@ -122,23 +128,76 @@ public: > virtual RefPtr<Image> currentFrameImage() { return nullptr; } > virtual void paintCurrentFrameInContext(GraphicsContext&, const FloatRect&) { } > >+ void setWidth(int); >+ void setHeight(int); >+ const IntSize& size() const { return m_size; } >+ virtual bool applySize(const IntSize&) { return false; } >+ >+ double frameRate() const { return m_frameRate; } >+ void setFrameRate(double); >+ virtual bool applyFrameRate(double) { return false; } >+ >+ double aspectRatio() const { return m_aspectRatio; } >+ void setAspectRatio(double); >+ virtual bool applyAspectRatio(double) { return false; } >+ >+ RealtimeMediaSourceSettings::VideoFacingMode facingMode() const { return m_facingMode; } >+ void setFacingMode(RealtimeMediaSourceSettings::VideoFacingMode); >+ virtual bool applyFacingMode(RealtimeMediaSourceSettings::VideoFacingMode) { return false; } >+ >+ double volume() const { return m_volume; } >+ void setVolume(double); >+ virtual bool applyVolume(double) { return false; } >+ >+ double sampleRate() const { return m_sampleRate; } >+ void setSampleRate(double); >+ virtual bool applySampleRate(double) { return false; } >+ >+ double sampleSize() const { return m_sampleSize; } >+ void setSampleSize(double); >+ virtual bool applySampleSize(double) { return false; } >+ >+ bool echoCancellation() const { return m_echoCancellation; } >+ void setEchoCancellation(bool); >+ virtual bool applyEchoCancellation(bool) { return false; } >+ > protected: > RealtimeMediaSource(const String& id, Type, const String& name); > >+ void scheduleDeferredTask(std::function<void()>&&); >+ > bool m_muted { false }; > > private: >+ WeakPtr<RealtimeMediaSource> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); } >+ >+ enum ConstraintSupport { Ignored, Supported, Unsupported }; >+ ConstraintSupport supportsConstraint(const MediaConstraint&); >+ void applyConstraint(const MediaConstraint&); >+ >+ WeakPtrFactory<RealtimeMediaSource> m_weakPtrFactory; >+ Lock m_lock; >+ > String m_id; > String m_persistentID; > Type m_type; > String m_name; >- bool m_stopped { false }; > Vector<Observer*> m_observers; >+ IntSize m_size; >+ double m_frameRate { 30 }; >+ double m_aspectRatio { 0 }; >+ double m_volume { 1 }; >+ double m_sampleRate { 0 }; >+ double m_sampleSize { 0 }; >+ unsigned m_fitnessScore { 0 }; >+ RealtimeMediaSourceSettings::VideoFacingMode m_facingMode { RealtimeMediaSourceSettings::User}; > >+ bool m_echoCancellation { false }; >+ bool m_stopped { false }; > bool m_readonly { false }; > bool m_remote { false }; >- >- unsigned m_fitnessScore { 0 }; >+ bool m_pendingSettingsDidChangeNotification { false }; >+ bool m_suppressNotifications { true }; > }; > > } // namespace WebCore >Index: Source/WebCore/platform/mediastream/RealtimeMediaSourceCapabilities.h >=================================================================== >--- Source/WebCore/platform/mediastream/RealtimeMediaSourceCapabilities.h (revision 205291) >+++ Source/WebCore/platform/mediastream/RealtimeMediaSourceCapabilities.h (working copy) >@@ -48,7 +48,7 @@ public: > Type type() const { return m_type; } > > union ValueUnion { >- unsigned long asULong; >+ int asInt; > double asDouble; > }; > >@@ -63,10 +63,10 @@ public: > m_minOrValue.asDouble = value; > } > >- CapabilityValueOrRange(unsigned long value) >+ CapabilityValueOrRange(int value) > : m_type(ULong) > { >- m_minOrValue.asULong = value; >+ m_minOrValue.asInt = value; > } > > CapabilityValueOrRange(double min, double max) >@@ -76,11 +76,11 @@ public: > m_max.asDouble = max; > } > >- CapabilityValueOrRange(unsigned long min, unsigned long max) >+ CapabilityValueOrRange(int min, int max) > : m_type(ULongRange) > { >- m_minOrValue.asULong = min; >- m_max.asULong = max; >+ m_minOrValue.asInt = min; >+ m_max.asInt = max; > } > > const ValueUnion& rangeMin() const >Index: Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.cpp >=================================================================== >--- Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.cpp (revision 205291) >+++ Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.cpp (working copy) >@@ -37,22 +37,40 @@ > > namespace WebCore { > >-const AtomicString& RealtimeMediaSourceSettings::facingMode(RealtimeMediaSourceSettings::VideoFacingMode mode) >+static const AtomicString& userFacing() > { > static NeverDestroyed<AtomicString> userFacing("user", AtomicString::ConstructFromLiteral); >+ return userFacing; >+} >+static const AtomicString& environmentFacing() >+{ > static NeverDestroyed<AtomicString> environmentFacing("environment", AtomicString::ConstructFromLiteral); >+ return environmentFacing; >+} >+ >+static const AtomicString& leftFacing() >+{ > static NeverDestroyed<AtomicString> leftFacing("left", AtomicString::ConstructFromLiteral); >+ return leftFacing; >+} >+ >+static const AtomicString& rightFacing() >+{ > static NeverDestroyed<AtomicString> rightFacing("right", AtomicString::ConstructFromLiteral); >- >+ return rightFacing; >+} >+ >+const AtomicString& RealtimeMediaSourceSettings::facingMode(RealtimeMediaSourceSettings::VideoFacingMode mode) >+{ > switch (mode) { > case RealtimeMediaSourceSettings::User: >- return userFacing; >+ return userFacing(); > case RealtimeMediaSourceSettings::Environment: >- return environmentFacing; >+ return environmentFacing(); > case RealtimeMediaSourceSettings::Left: >- return leftFacing; >+ return leftFacing(); > case RealtimeMediaSourceSettings::Right: >- return rightFacing; >+ return rightFacing(); > case RealtimeMediaSourceSettings::Unknown: > return emptyAtom; > } >@@ -61,6 +79,20 @@ const AtomicString& RealtimeMediaSourceS > return emptyAtom; > } > >+RealtimeMediaSourceSettings::VideoFacingMode RealtimeMediaSourceSettings::videoFacingModeEnum(const String& mode) >+{ >+ if (mode == userFacing()) >+ return RealtimeMediaSourceSettings::User; >+ if (mode == environmentFacing()) >+ return RealtimeMediaSourceSettings::Environment; >+ if (mode == leftFacing()) >+ return RealtimeMediaSourceSettings::Left; >+ if (mode == rightFacing()) >+ return RealtimeMediaSourceSettings::Right; >+ >+ return RealtimeMediaSourceSettings::Unknown; >+} >+ > } // namespace WebCore > > #endif // ENABLE(MEDIA_STREAM) >Index: Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.h >=================================================================== >--- Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.h (revision 205291) >+++ Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.h (working copy) >@@ -42,6 +42,8 @@ public: > > static const AtomicString& facingMode(RealtimeMediaSourceSettings::VideoFacingMode); > >+ static RealtimeMediaSourceSettings::VideoFacingMode videoFacingModeEnum(const String&); >+ > explicit RealtimeMediaSourceSettings() > { > } >Index: Source/WebCore/platform/mediastream/mac/AVAudioCaptureSource.mm >=================================================================== >--- Source/WebCore/platform/mediastream/mac/AVAudioCaptureSource.mm (revision 205291) >+++ Source/WebCore/platform/mediastream/mac/AVAudioCaptureSource.mm (working copy) >@@ -80,7 +80,7 @@ AVAudioCaptureSource::~AVAudioCaptureSou > void AVAudioCaptureSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities) > { > // FIXME: finish this implementation - https://webkit.org/b/122430 >- capabilities.setVolume(CapabilityValueOrRange(0, 1.0)); >+ capabilities.setVolume(CapabilityValueOrRange(0.0, 1.0)); > } > > void AVAudioCaptureSource::initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints& supportedConstraints) >Index: Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.h >=================================================================== >--- Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.h (revision 205291) >+++ Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.h (working copy) >@@ -33,7 +33,6 @@ > #include "Timer.h" > #include <wtf/Function.h> > #include <wtf/RetainPtr.h> >-#include <wtf/WeakPtr.h> > > OBJC_CLASS AVCaptureAudioDataOutput; > OBJC_CLASS AVCaptureConnection; >@@ -63,8 +62,6 @@ public: > void stopProducingData() override; > bool isProducingData() const override { return m_isRunning; } > >- WeakPtr<AVMediaCaptureSource> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); } >- > protected: > AVMediaCaptureSource(AVCaptureDevice*, const AtomicString&, RealtimeMediaSource::Type, PassRefPtr<MediaConstraints>); > >@@ -86,15 +83,12 @@ protected: > void setVideoSampleBufferDelegate(AVCaptureVideoDataOutput*); > void setAudioSampleBufferDelegate(AVCaptureAudioDataOutput*); > >- void scheduleDeferredTask(Function<void ()>&&); >- > private: > void setupSession(); > void reset() override; > > RealtimeMediaSourceSettings m_currentSettings; > RealtimeMediaSourceSupportedConstraints m_supportedConstraints; >- WeakPtrFactory<AVMediaCaptureSource> m_weakPtrFactory; > RetainPtr<WebCoreAVMediaCaptureSourceObserver> m_objcObserver; > RefPtr<MediaConstraints> m_constraints; > RefPtr<RealtimeMediaSourceCapabilities> m_capabilities; >Index: Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.mm >=================================================================== >--- Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.mm (revision 205291) >+++ Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.mm (working copy) >@@ -124,7 +124,6 @@ static dispatch_queue_t globaVideoCaptur > > AVMediaCaptureSource::AVMediaCaptureSource(AVCaptureDeviceTypedef* device, const AtomicString& id, RealtimeMediaSource::Type type, PassRefPtr<MediaConstraints> constraints) > : RealtimeMediaSource(id, type, emptyString()) >- , m_weakPtrFactory(this) > , m_objcObserver(adoptNS([[WebCoreAVMediaCaptureSourceObserver alloc] initWithCallback:this])) > , m_constraints(constraints) > , m_device(device) >@@ -239,17 +238,6 @@ void AVMediaCaptureSource::setAudioSampl > [audioOutput setSampleBufferDelegate:m_objcObserver.get() queue:globaAudioCaptureSerialQueue()]; > } > >-void AVMediaCaptureSource::scheduleDeferredTask(Function<void ()>&& function) >-{ >- ASSERT(function); >- callOnMainThread([weakThis = createWeakPtr(), function = WTFMove(function)] { >- if (!weakThis) >- return; >- >- function(); >- }); >-} >- > AudioSourceProvider* AVMediaCaptureSource::audioSourceProvider() > { > ASSERT_NOT_REACHED(); >Index: Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.h >=================================================================== >--- Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.h (revision 205291) >+++ Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.h (working copy) >@@ -57,10 +57,12 @@ private: > > void updateSettings(RealtimeMediaSourceSettings&) override; > >+ bool applySize(const IntSize&) override; >+ bool applyFrameRate(double) override; >+ > void initializeCapabilities(RealtimeMediaSourceCapabilities&) override; > void initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints&) override; > >- bool applyConstraints(MediaConstraints*); > bool setFrameRateConstraint(double minFrameRate, double maxFrameRate); > > bool updateFramerate(CMSampleBufferRef); >Index: Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm >=================================================================== >--- Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm (revision 205291) >+++ Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm (working copy) >@@ -128,91 +128,64 @@ void AVVideoCaptureSource::updateSetting > settings.setAspectRatio(static_cast<float>(m_width) / m_height); > } > >-bool AVVideoCaptureSource::setFrameRateConstraint(double minFrameRate, double maxFrameRate) >+bool AVVideoCaptureSource::applySize(const IntSize& size) > { >- AVFrameRateRange *bestFrameRateRange = 0; >- >- for (AVFrameRateRange *frameRateRange in [[device() activeFormat] videoSupportedFrameRateRanges]) { >- if (!maxFrameRate) { >- if (minFrameRate == [frameRateRange minFrameRate]) >- bestFrameRateRange = frameRateRange; >- } else if (minFrameRate >= [frameRateRange minFrameRate] && maxFrameRate <= [frameRateRange maxFrameRate]) { >- if (CMTIME_COMPARE_INLINE([frameRateRange minFrameDuration], >, [bestFrameRateRange minFrameDuration])) >- bestFrameRateRange = frameRateRange; >- } >- } >- >- if (!bestFrameRateRange) { >- LOG(Media, "AVVideoCaptureSource::setFrameRateConstraint(%p), frame rate range %f..%f not supported by video device", this, minFrameRate, maxFrameRate); >+ NSString *preset = AVCaptureSessionInfo(session()).bestSessionPresetForVideoDimensions(size.width(), size.height()); >+ if (!preset || ![session() canSetSessionPreset:preset]) { >+ LOG(Media, "AVVideoCaptureSource::applySize%p), unable find or set preset for width: %i, height: %i", this, size.width(), size.height()); > return false; > } >- >+ > NSError *error = nil; > @try { >- if ([device() lockForConfiguration:&error]) { >- [device() setActiveVideoMinFrameDuration:[bestFrameRateRange minFrameDuration]]; >- if (maxFrameRate) >- [device() setActiveVideoMaxFrameDuration:[bestFrameRateRange maxFrameDuration]]; >- [device() unlockForConfiguration]; >- } >+ [session() setSessionPreset:preset]; >+ > } @catch(NSException *exception) { >- LOG(Media, "AVVideoCaptureSource::setFrameRateConstraint(%p), exception thrown configuring device: <%s> %s", this, [[exception name] UTF8String], [[exception reason] UTF8String]); >+ LOG(Media, "AVVideoCaptureSource::applySize(%p), exception thrown configuring device: <%s> %s", this, [[exception name] UTF8String], [[exception reason] UTF8String]); > return false; > } >- >+ > if (error) { >- LOG(Media, "AVVideoCaptureSource::setFrameRateConstraint(%p), failed to lock video device for configuration: %s", this, [[error localizedDescription] UTF8String]); >+ LOG(Media, "AVVideoCaptureSource::applySize(%p), failed to lock video device for configuration: %s", this, [[error localizedDescription] UTF8String]); > return false; > } > >- LOG(Media, "AVVideoCaptureSource::setFrameRateConstraint(%p) - set frame rate range to %f..%f", this, minFrameRate, maxFrameRate); >+ LOG(Media, "AVVideoCaptureSource::applySize(%p) - set frame size to %i x %i", this, size.width(), size.height()); > return true; > } > >-bool AVVideoCaptureSource::applyConstraints(MediaConstraints* constraints) >+bool AVVideoCaptureSource::applyFrameRate(double rate) > { >- ASSERT(constraints); >- >- // FIXME: Below needs to be refactored for https://bugs.webkit.org/show_bug.cgi?id=160579. >- >- auto& mandatoryConstraints = constraints->mandatoryConstraints(); >- int intValue; >- >- String widthConstraintName = RealtimeMediaSourceSupportedConstraints::nameForConstraint(MediaConstraintType::Width); >- auto widthConstraint = mandatoryConstraints.get(widthConstraintName); >- Optional<int> width; >- if (widthConstraint && widthConstraint->getExact(intValue)) >- width = intValue; >- >- String heightConstraintName = RealtimeMediaSourceSupportedConstraints::nameForConstraint(MediaConstraintType::Height); >- auto heightConstraint = mandatoryConstraints.get(heightConstraintName); >- Optional<int> height; >- if (heightConstraint && heightConstraint->getExact(intValue)) >- height = intValue; >- >- if (width && height) { >- NSString *preset = AVCaptureSessionInfo(session()).bestSessionPresetForVideoDimensions(width.value(), height.value()); >- if (!preset || ![session() canSetSessionPreset:preset]) { >- LOG(Media, "AVVideoCaptureSource::applyConstraints(%p), unable find or set preset for width: %i, height: %i", this, width.value(), height.value()); >- return false; >+ AVFrameRateRange *bestFrameRateRange = 0; >+ for (AVFrameRateRange *frameRateRange in [[device() activeFormat] videoSupportedFrameRateRanges]) { >+ if (rate >= [frameRateRange minFrameRate] && rate <= [frameRateRange maxFrameRate]) { >+ if (!bestFrameRateRange || CMTIME_COMPARE_INLINE([frameRateRange minFrameDuration], >, [bestFrameRateRange minFrameDuration])) >+ bestFrameRateRange = frameRateRange; > } >- >- [session() setSessionPreset:preset]; > } > >- String frameRateConstraintName = RealtimeMediaSourceSupportedConstraints::nameForConstraint(MediaConstraintType::FrameRate); >- auto frameRateConstraint = mandatoryConstraints.get(frameRateConstraintName); >+ if (!bestFrameRateRange) { >+ LOG(Media, "AVVideoCaptureSource::applyFrameRate(%p), frame rate %f not supported by video device", this, rate); >+ return false; >+ } > >- Optional<double> frameRate; >- double doubleValue; >- if (frameRateConstraint && frameRateConstraint->getExact(doubleValue)) >- frameRate = doubleValue; >+ NSError *error = nil; >+ @try { >+ if ([device() lockForConfiguration:&error]) { >+ [device() setActiveVideoMinFrameDuration:[bestFrameRateRange minFrameDuration]]; >+ [device() unlockForConfiguration]; >+ } >+ } @catch(NSException *exception) { >+ LOG(Media, "AVVideoCaptureSource::applyFrameRate(%p), exception thrown configuring device: <%s> %s", this, [[exception name] UTF8String], [[exception reason] UTF8String]); >+ return false; >+ } > >- if (frameRate && !setFrameRateConstraint(frameRate.value(), 0)) { >- LOG(Media, "AVVideoCaptureSource::applyConstraints(%p), unable set frame rate to %f", this, frameRate.value()); >+ if (error) { >+ LOG(Media, "AVVideoCaptureSource::applyFrameRate(%p), failed to lock video device for configuration: %s", this, [[error localizedDescription] UTF8String]); > return false; > } > >+ LOG(Media, "AVVideoCaptureSource::applyFrameRate(%p) - set frame rate range to %f", this, rate); > return true; > } > >@@ -227,9 +200,6 @@ void AVVideoCaptureSource::setupCaptureS > > [session() addInput:videoIn.get()]; > >- if (constraints()) >- applyConstraints(constraints()); >- > RetainPtr<AVCaptureVideoDataOutputType> videoOutput = adoptNS([allocAVCaptureVideoDataOutputInstance() init]); > RetainPtr<NSDictionary> settingsDictionary = adoptNS([[NSDictionary alloc] initWithObjectsAndKeys: > [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey >Index: Source/WebCore/platform/mock/MockRealtimeAudioSource.cpp >=================================================================== >--- Source/WebCore/platform/mock/MockRealtimeAudioSource.cpp (revision 205291) >+++ Source/WebCore/platform/mock/MockRealtimeAudioSource.cpp (working copy) >@@ -59,12 +59,13 @@ MockRealtimeAudioSource::MockRealtimeAud > > void MockRealtimeAudioSource::updateSettings(RealtimeMediaSourceSettings& settings) > { >- settings.setVolume(50); >+ settings.setVolume(volume()); >+ settings.setEchoCancellation(echoCancellation()); > } > > void MockRealtimeAudioSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities) > { >- capabilities.setVolume(CapabilityValueOrRange(0, 1.0)); >+ capabilities.setVolume(CapabilityValueOrRange(0.0, 1.0)); > capabilities.setEchoCancellation(RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite); > } > >Index: Source/WebCore/platform/mock/MockRealtimeAudioSource.h >=================================================================== >--- Source/WebCore/platform/mock/MockRealtimeAudioSource.h (revision 205291) >+++ Source/WebCore/platform/mock/MockRealtimeAudioSource.h (working copy) >@@ -51,6 +51,12 @@ protected: > MockRealtimeAudioSource(const String& name = ASCIILiteral("Mock audio device")); > > private: >+ >+ bool applyVolume(double) override { return true; } >+ bool applySampleRate(double) override { return true; } >+ bool applySampleSize(double) override { return true; } >+ bool applyEchoCancellation(bool) override { return true; } >+ > void updateSettings(RealtimeMediaSourceSettings&) override; > void initializeCapabilities(RealtimeMediaSourceCapabilities&) override; > void initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints&) override; >Index: Source/WebCore/platform/mock/MockRealtimeMediaSource.cpp >=================================================================== >--- Source/WebCore/platform/mock/MockRealtimeMediaSource.cpp (revision 205291) >+++ Source/WebCore/platform/mock/MockRealtimeMediaSource.cpp (working copy) >@@ -124,7 +124,6 @@ RealtimeMediaSourceSupportedConstraints& > return m_supportedConstraints; > } > >- > } // namespace WebCore > > #endif // ENABLE(MEDIA_STREAM) >Index: Source/WebCore/platform/mock/MockRealtimeMediaSource.h >=================================================================== >--- Source/WebCore/platform/mock/MockRealtimeMediaSource.h (revision 205291) >+++ Source/WebCore/platform/mock/MockRealtimeMediaSource.h (working copy) >@@ -37,8 +37,6 @@ > > namespace WebCore { > >-class FloatRect; >-class GraphicsContext; > class TrackSourceInfo; > > class MockRealtimeMediaSource : public RealtimeMediaSource { >Index: Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp >=================================================================== >--- Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp (revision 205291) >+++ Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp (working copy) >@@ -65,6 +65,7 @@ MockRealtimeVideoSource::MockRealtimeVid > : MockRealtimeMediaSource(createCanonicalUUIDString(), RealtimeMediaSource::Video, name) > , m_timer(RunLoop::current(), this, &MockRealtimeVideoSource::generateFrame) > { >+ setFrameRate(30); > m_dashWidths.reserveInitialCapacity(2); > m_dashWidths.uncheckedAppend(6); > m_dashWidths.uncheckedAppend(6); >@@ -73,11 +74,13 @@ MockRealtimeVideoSource::MockRealtimeVid > void MockRealtimeVideoSource::startProducingData() > { > MockRealtimeMediaSource::startProducingData(); >- if (m_size.isEmpty()) >- setSize(IntSize(640, 480)); >+ if (size().isEmpty()) { >+ setWidth(640); >+ setHeight(480); >+ } > > m_startTime = monotonicallyIncreasingTime(); >- m_timer.startRepeating(std::chrono::milliseconds(lround(1000 / m_frameRate))); >+ m_timer.startRepeating(std::chrono::milliseconds(lround(1000 / frameRate()))); > } > > void MockRealtimeVideoSource::stopProducingData() >@@ -98,19 +101,21 @@ double MockRealtimeVideoSource::elapsedT > > void MockRealtimeVideoSource::updateSettings(RealtimeMediaSourceSettings& settings) > { >- settings.setFacingMode(RealtimeMediaSourceSettings::User); >- settings.setFrameRate(m_frameRate); >- settings.setWidth(m_size.width()); >- settings.setHeight(m_size.height()); >- settings.setAspectRatio(static_cast<float>(m_size.width()) / m_size.height()); >+ settings.setFacingMode(facingMode()); >+ settings.setFrameRate(frameRate()); >+ IntSize size = this->size(); >+ settings.setWidth(size.width()); >+ settings.setHeight(size.height()); >+ if (aspectRatio()) >+ settings.setAspectRatio(aspectRatio()); > } > > void MockRealtimeVideoSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities) > { > capabilities.addFacingMode(RealtimeMediaSourceSettings::User); > capabilities.addFacingMode(RealtimeMediaSourceSettings::Environment); >- capabilities.setWidth(CapabilityValueOrRange(320UL, 1920UL)); >- capabilities.setHeight(CapabilityValueOrRange(240UL, 1080UL)); >+ capabilities.setWidth(CapabilityValueOrRange(320, 1920)); >+ capabilities.setHeight(CapabilityValueOrRange(240, 1080)); > capabilities.setFrameRate(CapabilityValueOrRange(15.0, 60.0)); > capabilities.setAspectRatio(CapabilityValueOrRange(4 / 3.0, 16 / 9.0)); > } >@@ -124,26 +129,19 @@ void MockRealtimeVideoSource::initialize > supportedConstraints.setSupportsFacingMode(true); > } > >-void MockRealtimeVideoSource::setFrameRate(float rate) >+bool MockRealtimeVideoSource::applyFrameRate(double rate) > { >- if (m_frameRate == rate) >- return; >- >- m_frameRate = rate; > if (m_timer.isActive()) >- m_timer.startRepeating(std::chrono::milliseconds(lround(1000 / m_frameRate))); >+ m_timer.startRepeating(std::chrono::milliseconds(lround(1000 / rate))); > >- settingsDidChange(); >+ updatePlatformLayer(); >+ updateSampleBuffer(); >+ return true; > } > >-void MockRealtimeVideoSource::setSize(const IntSize& size) >+bool MockRealtimeVideoSource::applySize(const IntSize& size) > { >- if (size == m_size) >- return; >- >- m_size = size; >- >- m_baseFontSize = m_size.height() * .08; >+ m_baseFontSize = size.height() * .08; > FontCascadeDescription fontDescription; > fontDescription.setOneFamily("Courier"); > fontDescription.setSpecifiedSize(m_baseFontSize); >@@ -168,13 +166,13 @@ void MockRealtimeVideoSource::setSize(co > m_imageBuffer = nullptr; > updatePlatformLayer(); > >- settingsDidChange(); >+ return true; > } > > void MockRealtimeVideoSource::drawAnimation(GraphicsContext& context) > { >- float radius = m_size.width() * .09; >- FloatPoint location(m_size.width() * .8, m_size.height() * .3); >+ float radius = size().width() * .09; >+ FloatPoint location(size().width() * .8, size().height() * .3); > > m_path.clear(); > m_path.moveTo(location); >@@ -184,7 +182,7 @@ void MockRealtimeVideoSource::drawAnimat > context.setFillRule(RULE_NONZERO); > context.fillPath(m_path); > >- float endAngle = piFloat * (((fmod(m_frameNumber, m_frameRate) + 0.5) * (2.0 / m_frameRate)) + 1); >+ float endAngle = piFloat * (((fmod(m_frameNumber, frameRate()) + 0.5) * (2.0 / frameRate())) + 1); > m_path.clear(); > m_path.moveTo(location); > m_path.addArc(location, radius, 1.5 * piFloat, endAngle, false); >@@ -202,11 +200,12 @@ void MockRealtimeVideoSource::drawBoxes( > static const RGBA32 red = 0xffff0000; > static const RGBA32 green = 0xff008000; > >- float boxSize = m_size.width() * .035; >- float boxTop = m_size.height() * .6; >+ IntSize size = this->size(); >+ float boxSize = size.width() * .035; >+ float boxTop = size.height() * .6; > > m_path.clear(); >- FloatRect frameRect(2, 2, m_size.width() - 3, m_size.height() - 3); >+ FloatRect frameRect(2, 2, size.width() - 3, size.height() - 3); > context.setStrokeColor(Color::white); > context.setStrokeThickness(3); > context.setLineDash(m_dashWidths, 0); >@@ -217,7 +216,7 @@ void MockRealtimeVideoSource::drawBoxes( > context.setLineDash(DashArray(), 0); > m_path.clear(); > m_path.moveTo(FloatPoint(0, boxTop + boxSize)); >- m_path.addLineTo(FloatPoint(m_size.width(), boxTop + boxSize)); >+ m_path.addLineTo(FloatPoint(size.width(), boxTop + boxSize)); > m_path.closeSubpath(); > context.setStrokeColor(Color::white); > context.setStrokeThickness(2); >@@ -264,7 +263,8 @@ void MockRealtimeVideoSource::drawText(G > unsigned minutes = seconds / 60 % 60; > unsigned hours = minutes / 60 % 60; > >- FloatPoint timeLocation(m_size.width() * .05, m_size.height() * .15); >+ IntSize size = this->size(); >+ FloatPoint timeLocation(size.width() * .05, size.height() * .15); > context.setFillColor(Color::white); > context.setTextDrawingMode(TextModeFill); > String string = String::format("%02u:%02u:%02u.%03u", hours, minutes, seconds, milliseconds % 1000); >@@ -274,16 +274,16 @@ void MockRealtimeVideoSource::drawText(G > timeLocation.move(0, m_baseFontSize); > context.drawText(m_timeFont, TextRun((StringView(string))), timeLocation); > >- FloatPoint statsLocation(m_size.width() * .65, m_size.height() * .75); >- string = String::format("Frame rate: %ufps", m_frameRate); >+ FloatPoint statsLocation(size.width() * .65, size.height() * .75); >+ string = String::format("Frame rate: %ffps", frameRate()); > context.drawText(m_statsFont, TextRun((StringView(string))), statsLocation); > >- string = String::format("Size: %u x %u", m_size.width(), m_size.height()); >+ string = String::format("Size: %u x %u", size.width(), size.height()); > statsLocation.move(0, m_statsFontSize); > context.drawText(m_statsFont, TextRun((StringView(string))), statsLocation); > > const char* camera; >- switch (settings().facingMode()) { >+ switch (facingMode()) { > case RealtimeMediaSourceSettings::User: > camera = "User facing"; > break; >@@ -304,7 +304,7 @@ void MockRealtimeVideoSource::drawText(G > statsLocation.move(0, m_statsFontSize); > context.drawText(m_statsFont, TextRun((StringView(string))), statsLocation); > >- FloatPoint bipBopLocation(m_size.width() * .6, m_size.height() * .6); >+ FloatPoint bipBopLocation(size.width() * .6, size.height() * .6); > unsigned frameMod = m_frameNumber % 60; > if (frameMod <= 15) { > context.setFillColor(Color::gray); >@@ -319,11 +319,16 @@ void MockRealtimeVideoSource::drawText(G > > void MockRealtimeVideoSource::generateFrame() > { >- GraphicsContext& context = imageBuffer()->context(); >+ ImageBuffer* buffer = imageBuffer(); >+ if (!buffer) >+ return; >+ >+ GraphicsContext& context = buffer->context(); > GraphicsContextStateSaver stateSaver(context); > >- FloatRect frameRect(FloatPoint(), m_size); >- context.fillRect(FloatRect(FloatPoint(), m_size), Color::black); >+ IntSize size = this->size(); >+ FloatRect frameRect(FloatPoint(), size); >+ context.fillRect(FloatRect(FloatPoint(), size), Color::black); > > drawText(context); > drawAnimation(context); >@@ -338,7 +343,7 @@ ImageBuffer* MockRealtimeVideoSource::im > if (m_imageBuffer) > return m_imageBuffer.get(); > >- m_imageBuffer = ImageBuffer::create(m_size, Unaccelerated); >+ m_imageBuffer = ImageBuffer::create(size(), Unaccelerated); > if (!m_imageBuffer) > return nullptr; > >@@ -368,7 +373,6 @@ RefPtr<Image> MockRealtimeVideoSource::c > return m_imageBuffer->copyImage(DontCopyBackingStore); > } > >- > } // namespace WebCore > > #endif // ENABLE(MEDIA_STREAM) >Index: Source/WebCore/platform/mock/MockRealtimeVideoSource.h >=================================================================== >--- Source/WebCore/platform/mock/MockRealtimeVideoSource.h (revision 205291) >+++ Source/WebCore/platform/mock/MockRealtimeVideoSource.h (working copy) >@@ -51,11 +51,6 @@ public: > > virtual ~MockRealtimeVideoSource() { } > >- void setSize(const IntSize&); >- const IntSize& size() const { return m_size; } >- >- void setFrameRate(float); >- > protected: > MockRealtimeVideoSource(const String& name = ASCIILiteral("Mock video device")); > virtual void updatePlatformLayer() const { } >@@ -70,13 +65,18 @@ private: > void initializeCapabilities(RealtimeMediaSourceCapabilities&) override; > void initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints&) override; > >- void startProducingData() override; >- void stopProducingData() override; >+ void startProducingData() final; >+ void stopProducingData() final; > > void drawAnimation(GraphicsContext&); > void drawText(GraphicsContext&); > void drawBoxes(GraphicsContext&); > >+ bool applySize(const IntSize&) override; >+ bool applyFrameRate(double) override; >+ bool applyFacingMode(RealtimeMediaSourceSettings::VideoFacingMode) override { return true; } >+ bool applyAspectRatio(double) override { return true; } >+ > PlatformLayer* platformLayer() const override { return nullptr; } > RefPtr<Image> currentFrameImage() override; > void paintCurrentFrameInContext(GraphicsContext&, const FloatRect&) override; >@@ -94,14 +94,12 @@ private: > > mutable std::unique_ptr<ImageBuffer> m_imageBuffer; > >- IntSize m_size; > Path m_path; > DashArray m_dashWidths; > > double m_startTime { NAN }; > double m_elapsedTime { 0 }; > >- unsigned m_frameRate { 30 }; > unsigned m_frameNumber { 0 }; > > RunLoop::Timer<MockRealtimeVideoSource> m_timer; >Index: LayoutTests/ChangeLog >=================================================================== >--- LayoutTests/ChangeLog (revision 205291) >+++ LayoutTests/ChangeLog (working copy) >@@ -1,3 +1,17 @@ >+2016-09-01 Eric Carlson <eric.carlson@apple.com> >+ >+ [MediaStream] applyConstraints pt. 1 - mandatory constraints >+ https://bugs.webkit.org/show_bug.cgi?id=161469 >+ <rdar://problem/28109325> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * fast/mediastream/apply-constraints-audio-expected.txt: Added. >+ * fast/mediastream/apply-constraints-audio.html: Added. >+ * fast/mediastream/apply-constraints-video-expected.txt: Added. >+ * fast/mediastream/apply-constraints-video.html: Added. >+ * fast/mediastream/resources/apply-constraints-utils.js: Added. >+ > 2016-09-01 Carlos Garcia Campos <cgarcia@igalia.com> > > Unreviewed. Skip fixed layout tests in GTK+ >Index: LayoutTests/fast/mediastream/apply-constraints-audio-expected.txt >=================================================================== >--- LayoutTests/fast/mediastream/apply-constraints-audio-expected.txt (nonexistent) >+++ LayoutTests/fast/mediastream/apply-constraints-audio-expected.txt (working copy) >@@ -0,0 +1,43 @@ >+Tests applyConstraints on an audio stream track. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS stream.getVideoTracks().length is 0 >+PASS stream.getAudioTracks().length is 1 >+PASS video.videoTracks.length is 0 >+PASS video.audioTracks.length is 1 >+ >+** Constraint: {"volume":0.5} - set volume to a valid value. >+PASS settings['volume'] is 0.5 >+PASS typeof settings['echoCancellation'] is 'boolean' >+PASS settings['echoCancellation'] is false >+ >+** Constraint: {"volume":{"exact":2.1}} - the 'exact' constraint it too big, promise should reject and no settings should change. >+PASS Promise was rejected >+PASS error.constraint is "volume" >+PASS settings['volume'] is 0.5 >+PASS typeof settings['echoCancellation'] is 'boolean' >+PASS settings['echoCancellation'] is false >+ >+** Constraint: {"volume":{"exact":-1}} - the 'exact' constraint it too small, promise should reject and no settings should change. >+PASS Promise was rejected >+PASS error.constraint is "volume" >+PASS settings['volume'] is 0.5 >+PASS typeof settings['echoCancellation'] is 'boolean' >+PASS settings['echoCancellation'] is false >+ >+** Constraint: {"echoCancellation":true} - set echoCancellation to a valid value. >+PASS settings['volume'] is 0.5 >+PASS typeof settings['echoCancellation'] is 'boolean' >+PASS settings['echoCancellation'] is true >+ >+** Constraint: {"facingMode":"environment","frameRate":30,"volume":1} - constraint not supported by an audio track should be ignored. >+PASS settings['volume'] is 1 >+PASS typeof settings['echoCancellation'] is 'boolean' >+PASS settings['echoCancellation'] is true >+ >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/fast/mediastream/apply-constraints-audio.html >=================================================================== >--- LayoutTests/fast/mediastream/apply-constraints-audio.html (nonexistent) >+++ LayoutTests/fast/mediastream/apply-constraints-audio.html (working copy) >@@ -0,0 +1,63 @@ >+<!DOCTYPE html> >+<html> >+ <head> >+ <script src="../../resources/js-test-pre.js"></script> >+ <script src="resources/apply-constraints-utils.js"></script> >+ <script> >+ >+ let tests = [ >+ { >+ message: "set volume to a valid value.", >+ constraint: { volume: .5 }, >+ expected: { volume: .5, echoCancellation: false }, >+ }, >+ { >+ message: "the 'exact' constraint it too big, promise should reject and no settings should change.", >+ constraint: { volume: { exact: 2.1 } }, >+ expected: { volume: .5, echoCancellation: false }, >+ error: "volume", >+ }, >+ { >+ message: "the 'exact' constraint it too small, promise should reject and no settings should change.", >+ constraint: { volume: { exact: -1 } }, >+ expected: { volume: .5, echoCancellation: false }, >+ error: "volume", >+ }, >+ { >+ message: "set echoCancellation to a valid value.", >+ constraint: { echoCancellation: true }, >+ expected: { volume: .5, echoCancellation: true }, >+ }, >+ { >+ message: "constraint not supported by an audio track should be ignored.", >+ constraint: { facingMode: "environment", frameRate: 30, volume: 1.0 }, >+ expected: { volume: 1.0, echoCancellation: true }, >+ }, >+ >+ ]; >+ >+ let tester = new ConstraintsTest({ audio: true }, tests, "Tests applyConstraints on an audio stream track.") >+ .onStreamReady((s) => { >+ stream = s; >+ shouldBe('stream.getVideoTracks().length', '0'); >+ shouldBe('stream.getAudioTracks().length', '1'); >+ tester.setStreamTrack(stream.getAudioTracks()[0]); >+ }) >+ .onVideoReady((v) => { >+ video = v; >+ shouldBe('video.videoTracks.length', '0'); >+ shouldBe('video.audioTracks.length', '1'); >+ }) >+ .start(); >+ >+ </script> >+ <script src="../../resources/js-test-post.js"></script> >+ </head> >+ <body> >+ <video controls id="video"</video> >+ <br> >+ <div id="div"></div> >+ >+ </body> >+</html> >+ >Index: LayoutTests/fast/mediastream/apply-constraints-video-expected.txt >=================================================================== >--- LayoutTests/fast/mediastream/apply-constraints-video-expected.txt (nonexistent) >+++ LayoutTests/fast/mediastream/apply-constraints-video-expected.txt (working copy) >@@ -0,0 +1,97 @@ >+Tests applyConstraints on a video stream track. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS stream.getVideoTracks().length is 1 >+PASS stream.getAudioTracks().length is 0 >+PASS video.videoTracks.length is 1 >+PASS video.audioTracks.length is 0 >+ >+** Constraint: {"width":640,"height":480,"frameRate":30} - set width, height, and frame rate to valid values. >+PASS settings['width'] is 640 >+PASS settings['height'] is 480 >+PASS settings['frameRate'] is 30 >+ >+** Constraint: {"width":320,"height":240} - change width and height, frame rate should remain unchanged. >+PASS settings['width'] is 320 >+PASS settings['height'] is 240 >+PASS settings['frameRate'] is 30 >+ >+** Constraint: {"width":{"exact":2000}} - the 'exact' constraint can't be satisfied, promise should reject and no settings should change. >+PASS Promise was rejected >+PASS error.constraint is "width" >+PASS settings['width'] is 320 >+PASS settings['height'] is 240 >+PASS settings['frameRate'] is 30 >+ >+** Constraint: {"width":640,"height":{"min":3000}} - the 'min' constraint can't be satisfied, promise should reject and no settings should change. >+PASS Promise was rejected >+PASS error.constraint is "height" >+PASS settings['width'] is 320 >+PASS settings['height'] is 240 >+PASS settings['frameRate'] is 30 >+ >+** Constraint: {"frameRate":{"max":6}} - the 'max' constraint can't be satisfied, promise should reject and no settings should change. >+PASS Promise was rejected >+PASS error.constraint is "frameRate" >+PASS settings['frameRate'] is 30 >+ >+** Constraint: {"width":{"exact":400}} - the 'exact' constraint can be satisfied. >+PASS settings['width'] is 400 >+PASS settings['height'] is 240 >+ >+** Constraint: {"width":{"min":300,"ideal":5000}} - the 'ideal' constraint can't be satisfied but the 'min' can, max should be chosen. >+PASS settings['width'] is 1920 >+PASS settings['height'] is 240 >+ >+** Constraint: {"width":{"min":320,"ideal":1280},"height":{"min":480,"ideal":720}} - 'ideal' and 'min' constraints can be satisfied, 'ideal' should be chosen. >+PASS settings['width'] is 1280 >+PASS settings['height'] is 720 >+ >+** Constraint: {"width":3000} - ideal width is greater than track capability, should be clamped to the maximum value. >+PASS settings['width'] is 1920 >+ >+** Constraint: {"width":160,"height":120,"frameRate":10} - all values are less than track capabilities, should be clamped to the minimum values. >+PASS settings['width'] is 320 >+PASS settings['height'] is 240 >+PASS settings['frameRate'] is 15 >+ >+** Constraint: {"facingMode":"environment"} - set facing mode, width and height should remain unchanged >+PASS settings['width'] is 320 >+PASS settings['height'] is 240 >+PASS settings['facingMode'] is "environment" >+ >+** Constraint: {"facingMode":"USER","height":400} - illegal facing mode value should be ignored, height should change. >+PASS settings['facingMode'] is "environment" >+PASS settings['width'] is 320 >+PASS settings['height'] is 400 >+ >+** Constraint: {"FACINGMODE":"user","frameRate":30} - unknown constraint should be ignored, frame rate should change. >+PASS settings['facingMode'] is "environment" >+PASS settings['frameRate'] is 30 >+ >+** Constraint: {"aspectRatio":"1.3333"} - aspect ratio should change width and height. >+PASS settings['width'] is 320 >+PASS settings['height'] is 240 >+ >+** Constraint: {"aspectRatio":"1.7778"} - new aspect ratio should again change width and height. >+PASS settings['width'] is 320 >+PASS settings['height'] is 180 >+ >+** Constraint: {"width":1920} - when aspect ratio has been set, changing width should change height. >+PASS settings['width'] is 1920 >+PASS settings['height'] is 1080 >+ >+** Constraint: {"height":576} - when aspect ratio has been set, changing height should change width. >+PASS settings['width'] is 1024 >+PASS settings['height'] is 576 >+ >+** Constraint: {"deviceId":{"exact":"20983-20o198-109283-098-09812"}} - the 'exact' deviceID doesn't match, promise should reject. >+PASS Promise was rejected >+PASS error.constraint is "deviceId" >+ >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/fast/mediastream/apply-constraints-video.html >=================================================================== >--- LayoutTests/fast/mediastream/apply-constraints-video.html (nonexistent) >+++ LayoutTests/fast/mediastream/apply-constraints-video.html (working copy) >@@ -0,0 +1,129 @@ >+<!DOCTYPE html> >+<html> >+ <head> >+ <script src="../../resources/js-test-pre.js"></script> >+ <script src="resources/apply-constraints-utils.js"></script> >+ <script> >+ >+ let tests = [ >+ { >+ message: "set width, height, and frame rate to valid values.", >+ constraint: { width: 640, height: 480, frameRate: 30 }, >+ expected: { width: 640, height: 480, frameRate: 30 }, >+ }, >+ { >+ message: "change width and height, frame rate should remain unchanged.", >+ constraint: { width: 320, height: 240 }, >+ expected: { width: 320, height: 240, frameRate: 30 }, >+ }, >+ { >+ message: "the 'exact' constraint can't be satisfied, promise should reject and no settings should change.", >+ constraint: { width: { exact: 2000 } }, >+ expected: { width: 320, height: 240, frameRate: 30 }, >+ error: "width", >+ }, >+ { >+ message: "the 'min' constraint can't be satisfied, promise should reject and no settings should change.", >+ constraint: { width: 640, height: {min: 3000}, }, >+ expected: { width: 320, height: 240, frameRate: 30 }, >+ error: "height", >+ }, >+ { >+ message: "the 'max' constraint can't be satisfied, promise should reject and no settings should change.", >+ constraint: { frameRate: {max: 6}, }, >+ expected: { frameRate: 30 }, >+ error: "frameRate", >+ }, >+ { >+ message: "the 'exact' constraint can be satisfied.", >+ constraint: { width: { exact: 400 } }, >+ expected: { width: 400, height: 240 }, >+ }, >+ { >+ message: "the 'ideal' constraint can't be satisfied but the 'min' can, max should be chosen.", >+ constraint: { width: {min: 300, ideal: 5000} }, >+ expected: { width: 1920, height: 240 }, >+ }, >+ { >+ message: "'ideal' and 'min' constraints can be satisfied, 'ideal' should be chosen.", >+ constraint: { width: {min: 320, ideal: 1280}, height: {min: 480, ideal: 720}, }, >+ expected: { width: 1280, height: 720 }, >+ }, >+ { >+ message: "ideal width is greater than track capability, should be clamped to the maximum value.", >+ constraint: { width: 3000 }, >+ expected: { width: 1920}, >+ }, >+ { >+ message: "all values are less than track capabilities, should be clamped to the minimum values.", >+ constraint: { width: 160, height: 120, frameRate: 10 }, >+ expected: { width: 320, height: 240, frameRate: 15 }, >+ }, >+ { >+ message: "set facing mode, width and height should remain unchanged", >+ constraint: { facingMode: "environment" }, >+ expected: { width: 320, height: 240, facingMode: "environment" }, >+ }, >+ { >+ message: "illegal facing mode value should be ignored, height should change.", >+ constraint: { facingMode: "USER", height: 400 }, >+ expected: { facingMode: "environment", width: 320, height: 400 }, >+ }, >+ { >+ message: "unknown constraint should be ignored, frame rate should change.", >+ constraint: { FACINGMODE: "user", frameRate: 30 }, >+ expected: { facingMode: "environment", frameRate: 30 }, >+ }, >+ { >+ message: "aspect ratio should change width and height.", >+ constraint: { aspectRatio: (4/3).toFixed(4) }, >+ expected: { width: 320, height: 240 }, >+ }, >+ { >+ message: "new aspect ratio should again change width and height.", >+ constraint: { aspectRatio: (16/9).toFixed(4) }, >+ expected: { width: 320, height: 180 }, >+ }, >+ { >+ message: "when aspect ratio has been set, changing width should change height.", >+ constraint: { width: 1920 }, >+ expected: { width: 1920, height: 1080}, >+ }, >+ { >+ message: "when aspect ratio has been set, changing height should change width.", >+ constraint: { height: 576 }, >+ expected: { width: 1024, height: 576}, >+ }, >+ { >+ message: "the 'exact' deviceID doesn't match, promise should reject.", >+ constraint: { deviceId: {exact: "20983-20o198-109283-098-09812"}, }, >+ expected: { }, >+ error: "deviceId", >+ }, >+ ]; >+ >+ let tester = new ConstraintsTest({ video: true }, tests, "Tests applyConstraints on a video stream track.") >+ .onStreamReady((s) => { >+ stream = s; >+ shouldBe('stream.getVideoTracks().length', '1'); >+ shouldBe('stream.getAudioTracks().length', '0'); >+ tester.setStreamTrack(stream.getVideoTracks()[0]); >+ }) >+ .onVideoReady((v) => { >+ video = v; >+ shouldBe('video.videoTracks.length', '1'); >+ shouldBe('video.audioTracks.length', '0'); >+ }) >+ .start(); >+ >+ </script> >+ <script src="../../resources/js-test-post.js"></script> >+ </head> >+ <body> >+ <video controls id="video"</video> >+ <br> >+ <div id="div"></div> >+ >+ </body> >+</html> >+ >Index: LayoutTests/fast/mediastream/resources/apply-constraints-utils.js >=================================================================== >--- LayoutTests/fast/mediastream/resources/apply-constraints-utils.js (nonexistent) >+++ LayoutTests/fast/mediastream/resources/apply-constraints-utils.js (working copy) >@@ -0,0 +1,115 @@ >+let settings; >+let error; >+ >+ConstraintsTest = class ConstraintsTest { >+ >+ constructor(constraints, tests, description) >+ { >+ this.constraints = constraints; >+ this.tests = tests; >+ this.description = description; >+ >+ window.jsTestIsAsync = true; >+ window.successfullyParsed = true; >+ if (window.testRunner) >+ testRunner.setUserMediaPermission(true); >+ } >+ >+ onStreamReady(callback) >+ { >+ if (typeof callback == "function") >+ this.streamCallback = callback; >+ return this; >+ } >+ >+ onVideoReady(callback) >+ { >+ if (typeof callback == "function") >+ this.videoCallback = callback; >+ return this; >+ } >+ >+ scheduleNextTest() >+ { >+ new Promise(resolved => this.runNextTest()); >+ } >+ >+ checkTrackSettings() >+ { >+ settings = this.track.getSettings(); >+ for (let property in this.currentTest.expected) { >+ let expected = this.currentTest.expected[property]; >+ if (typeof expected === "string") >+ shouldBeEqualToString(`settings['${property}']`, expected); >+ else >+ shouldEvaluateTo(`settings['${property}']`, expected); >+ } >+ } >+ >+ runNextTest() >+ { >+ description(this.description); >+ >+ debug(""); >+ if (!this.tests.length) { >+ finishJSTest(); >+ return; >+ } >+ >+ this.currentTest = this.tests.shift(); >+ debug(`** Constraint: ${JSON.stringify(this.currentTest.constraint)} - ${this.currentTest.message}`); >+ this.track.applyConstraints(this.currentTest.constraint) >+ .then(() => { >+ if (this.currentTest.error) >+ testFailed(`Constraint '${this.currentTest.error}' should have failed to apply, is '${settings[this.currentTest.error]}'`); >+ else >+ this.checkTrackSettings() >+ this.scheduleNextTest(); >+ }) >+ .catch((evt) => { >+ if (!this.currentTest.error) { >+ testFailed("Promise was rejected"); >+ testFailed(`Constraint failed to apply: ${evt} - constraint = ${evt.constraint}`); >+ } else { >+ testPassed("Promise was rejected"); >+ error = evt; >+ shouldBeEqualToString("error.constraint", this.currentTest.error); >+ } >+ this.checkTrackSettings() >+ this.scheduleNextTest(); >+ }); >+ } >+ >+ setStreamTrack(track) >+ { >+ this.track = track; >+ } >+ >+ start() >+ { >+ window.addEventListener("load", function () { >+ >+ navigator.mediaDevices.getUserMedia(this.constraints) >+ .then(stream => { >+ this.video = document.querySelector("video"); >+ this.video.srcObject = stream; >+ if (this.streamCallback) >+ this.streamCallback(stream); >+ }) >+ .then(() => new Promise(resolve => this.video.onloadedmetadata = resolve)) >+ .then(() => { >+ if (this.videoCallback) >+ this.videoCallback(this.video); >+ this.runNextTest(); >+ }) >+ .catch(err => { >+ testFailed(`Stream setup failed with error: ${err}`); >+ finishJSTest(); >+ }); >+ >+ }.bind(this), false); >+ >+ return this; >+ } >+ >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 161469
:
287618
|
287626
|
287628
|
287630
|
287653
|
287656
|
287769
|
287779