From 9855d648fe81f8b060601245a8621d86ef71735c Mon Sep 17 00:00:00 2001 From: roytam1 Date: Tue, 9 May 2023 11:34:56 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1144107 - Part 4: Prevent assert when hitting EOS. r=kentuckyfriedtakahe (4e66e9d148) - Bug 1226842: Error rather than asserting when encountering error in sample table. r=gerald (4bbe6cd7f2) - Bug 1189992: Don't assume the last chunk always contains the sample we're looking for. r=gerald (4cf3a3f63a) - Bug 1225703 - Update in-tree libcubeb. r=padenot (49ce53e3c8) - Bug 1203449 - Remove OutputStreamListener from DecodedStream.cpp. r=roc. (24421a02e1) - Bug 1207915 - Apply the fix of bug 1052206 to DecodedStream. r=roc. (c73a3aecc6) - Bug 1218311 - Port the fix of bug 1193614 to VideoSink. r=cpearce. (cbbe3dc1ab) - Bug 1230338 - Record video frames dropped by the compositor, or while flushing during skip-to-keyframe. r=jya (edf53548d3) - Bug 1230882. Part 1 - destroy DecodedStreamData properly when dispatch fails. r=roc. (adb3c05671) - Bug 1230004. Part 1 - cache data in MDSM so it won't need to ask MediaDecoder. r=cpearce. (728c6b3e82) - Bug 1219163. Part 1 - Remove unused functions from AbstractMediaDecoder. r=jya. (f1dba3d50c) - Bug 1219163. Part 2 - Move some functions that are never called from the interface of AbstractMediaDecoder down the class hierarchy. r=jya. (c0fc72790f) - Bug 1226569. Part 1 - Use MediaEventSource to publish MetadataLoaded and FirstFrameLoaded events. r=jya. (76195567a5) - Bug 1226569. Part 2 - assert functions that should never be called after shutdown. r=jya. (672df604e5) - Bug 1227797 - Use MediaEventSource to publish playback events for MDSM. r=jya. (4a3fda2f34) - Bug 1228923 - Merge some MediaEventSource for MDSM. r=jya. (31929f5777) - Bug 1228939 - 1. add mSeekable to MediaInfo. 2. use MediaEventSource to notify the decoder when the media is not seekable. 3. remove unused code. r=jya. (38fb6d1099) - Bug 1230004. Part 2 - have MDSM::BeginShutdown return a promise and remove MDSM::mDecoder. r=cpearce. (0f45558e15) - add some disabled EME stuff (c4fa7e65be) - Bug 873438 - Implement IAccessible2_2::accessibleWithCaret, r=yzen (9fb71ce7f3) - Bug 1159872 - Make IAccessible2::Get_States once again return S_OK when it detects a defunct accessible. r=surkov (91ad6d1798) - bug 606080 - on windows give accessibles a unique 32 bit id r=surkov (848c16f16c) - Bug 1220897 - fix IAccessible2::get_accessibleWithCaret, r=tbsaunde (492790b4e1) - Bug 1225298 - Use GCHashMap for UniqueIdMap, r=terrence (ee896fb807) - Bug 1227567 - Optimise module namespace imports in Ion where we have type information r=shu (19fdf97bc3) - Bug 1230337 - Fix TypedArrayObject::isNeutered to stop calling typed arrays using inline storage neutered. (r=Waldo) (2838830319) - Bug 1055472 - Part 1: Factor out GetPrototypeFromConstructor and use it in existing object creation paths. (r=Waldo) (a26c188064) - Bug 1055472 - Part 2: Make the Function constructor properly subclassable. (r=Waldo) (e2ff4b48d4) - Bug 1055472 - Part 2b: Make Function.prototype.bind play nicely with subclassed functions. (r=Waldo, shoutouts for good flyby by evilpie) (59b347ced4) - Bug 1055472 - Part 3: Make the Object constructor properly subclassable. (r=Waldo) (aa23b6c005) - Bug 1055472 - Part 4: Make the Boolean constructor properly subclassable. (r=Waldo) (6ff68c5198) - Bug 1055472 - Part 5: Make the various Error constructors properly subclassable. (r=Waldo) (7e36f26bcd) - Bug 1055472 - Part 6: Make the Number constructor properly subclassable. (r=Waldo) (5b081bb54c) - Bug 1055472 - Part 7: Make the Date constructor properly subclassable. (r=Waldo) (a841523521) - Bug 1055472 - Part 8 prelim: Rename InitializeRegExp to RegExpObject::initFromAtom for readability. (r=Waldo) (053ce20768) - Bug 1055472 - Part 8: Make the RegExp constructor properly subclassable. (r=Waldo) (a522ecca25) - Bug 1055472 - Part 9: Make the Map constructor properly subclassable. (r=Waldo) (a29bb1408a) - Bug 1055472 - Part 10: Make the Set constructor properly subclassable. (r=Waldo) (82befdc508) - Bug 1055472 - Part 11: Make the WeakMap constructor properly subclassable. (r=Waldo) (0ea50757d4) - Bug 1055472 - Part 12: Mae the WeakSet constructor properly subclassable. (r=Waldo) (6ba67d4fe6) - Bug 1055472 - Part 13: Make the ArrayBuffer constructor properly subclassable. (r=Waldo) (7811dadf92) - Bug 1055472 - Part 14: Make the various TypedArray constructors properly subclassable. (r=Waldo, r=bhackett) (6bc10f8ec1) - Bug 1055472 - Part 15: Make the DataView constructor properly subclassable. (r=jorendorff, r=bhackett) (b2b6aef1f6) - Bug 1055472 - Part 16: Make the String constructor properly subclassable. (r=Waldo) (6bed2e45bc) - Bug 1055472 - Part 17: Make the Array constructor properly subclassable. (r=jorendorff, r=bhackett, r=terrence) (d301fe9814) - Bug 1055472 - Part 18: Incorportate arai's test into the subclassing suite. (r=me) (cfbc97738a) - Bug 1226416 - Expose a method to get a node's set of immediately dominated nodes in the dominator tree; r=bz,sfink (8226985fb6) - Bug 1199422 - Stop pattern matching class-constructors in String#replace and Array#sort. r=efaust (58b56ad143) - Bug 1206308 - include nsprpub and zlib for Windows, plus minor enhancements to make-source-package.sh, r=sstangl (e933f3a27a) - Bug 1229729 - Make make-source-package.sh work on OS X. r=sfink (d55f9bc552) - Bug 1221747 - Make JS_vsnprintf() return a value that indicates failure if we reach the buffer limit r=nbp (1bc82ccafd) - Bug 1227385 - Properly propagate $DIST from top-level after bug 1224490. r=mshal (ee4fc81981) - Bug 1200304 - Move stumbling code from gonkgps to MozStumbler.cpp. r=jdm (89aea7211c) - Bug 1207266 - turn off b2g stumbler if no RIL. r=jdm (ae42aeb5a2) - Bug 1228947 - Replace mfbt/Constants.h with math.h. r=roc (d56e2bbe2f) - Bug 1176261 - Discard gps with impossibly tiny accuracy. r=jaliu (93a4114ac7) - Bug 772750 - Get mobile connection technology from MobileConnectionService. r=garvank,echen (742d68becb) - Bug 1168068 - GonkGPSGeolocationProvider should use mRilDataServiceId in SetReferenceLocation. r=garvank (b07ee26cc4) - Bug 1227385 - Avoid make variable references in VISIBILITY_FLAGS and STL_FLAGS. r=mshal (e3e7fc9d99) - Bug 1225298 - Use GCHashSet for atoms table, r=terrence (4bce42238c) - Bug 1225298 - Use GCHashSet for InnerViewTable, r=terrence (e8da10443c) - Bug 1203297 - Fix one last bit of unified bustage; r=bbouvier (7395aca8da) - Bug 1228340: Rename JitOptions into DefaultJitOptions and get rid of the js_ prefix for the instance; r=h4writer (19777f9a49) - Bug 1227190 part 1. change PrepareScriptEnvironmentAndInvoke to return void, not bool, to make it clearer that it reports exceptions for you. r=jorendorff (7525efb3ad) - Bug 1227190 part 2. Change PrepareScriptEnvironmentAndInvoke to take a JSContext*, not a JSRuntime*. r=jorendorff (01b69ccab1) - Bug 1227190 part 3. Make debugger error reporting play nice with the embedding taking ownership of error reporting. r=jorendorff (008195b9a3) - Bug 1221600 - Tweak comments about standard internal methods in jsobj.h, jsfriendapi.h. r=efaust. (c1a4b0e2bf) - Bug 1223372 - Handle ToWindowProxyIfWindow returning a dead wrapper in PrepareForWrapping. r=bholley (0b3dd8084b) - Bug 1197095 - ensure that ForOfIterator does not pass arguments to next calls; r=evilpie (29acf9b12d) - Bug 1197094 - ForOfIterator calls ToObject on iterable; r=evilpie (533dd11111) - Bug 1216379 - Throw less cryptic error message when using a non-iterable in a for...of loop. r=jorendorff (ae7d4c3734) - Bug 1230010 - OdinMonkey: MIPS: Make immediate patching shared. r=luke (97b5650d3a) - Bug 1131759 - Atomicize SharedScriptData::marked. (r=terrence) (bafe64c080) - Bug 1229579 - Make class members properly XDR-able. (r=billm) (d95743694f) - Bug 1227642: Make data a ScopedJSFreePtr in js::detail::CopyScript to ensure it doesn't leak; r=jonco (db8bcafc09) - Bug 1227287 - Fix minor bug with f.arguments and lazy arguments. r=luke (9c1cc5aaad) - Bug 1228947 - mingw fixup. (3216b8df21) - Bug 1231163 - Don't assume the RNG's been initialized by a prior call to Math.random, when a call to Math.random is being inlined. (A method can be inlined once its identity has been guarded against, but mere identity can be established without the method having been called.) r=jwalden (d7e2235e63) - crash stuff (12dfaea226) - Bug 1218027 - "--with-system-icu fails: js/src/jsapi.cpp:69: error: visibility does not match previous declaration". r=mh+mozilla (5929f4280c) - Bug 1229740 - add to the list of wrapped system headers; r=glandium (990a0a22ab) - Bug 1192312 - Use MediaCodecSource for camera recording since gonkL r=aosmond (f68121e622) - Bug 1210293 - Remove legacy check from the XPIDL HTMLInputElement::SetUserInput. r=bz (f70fbfd8ea) - Bug 1220323 - Enable FasterMake backend by default for all apps. r=gps (7421f68979) - Fixup for b2g bustage from bug 1164921. r=me (6207a28039) - Bug 1209391 - Remove build/unix/uniq.py. r=mshal (d757f44938) - Bug 1192233 - delete check for sys/cdefs.h from configure; r=mshal (ca2709a478) - Bug 1191816 - don't check for sys/int_types.h in configure; r=mshal (3f77884338) - Bug 1182565 - Disable sandboxing on Linux Thread Sanitizer builds. r=kang (5dcd521ca1) - missing bits of Bug 1157768 - Build files and config headers for libav fft (5ab19c5158) - Bug 1149279 - Remove unused build config variable NSS_NO_LIBPKIX. r=gps (0abbcd89c3) - Bug 1161238 - Remove --disable-logging. r=gps (b896b8f77d) - Bug 1167201 - Make absence of gconf developer packages a fatal error rather than disabling gconf automatically in the build system. This is necessary because if gconf is disabled, many tests don't work because they use gconf to detect linux. r=glandium (4def45dc4f) - bug 1220037 pack and unpack Nyquist for MOZ_LIBAV_FFT r=padenot (23e4e277ff) - bug 1220041 remove unnecessary temp buffer and copy with libav FFT r=padenot (20df464d7b) - bug 1220041 scale when copying for inverse FFT to avoid iterating twice r=padenot (d9c11db7e2) - bug 1223520 avoid leaking NaNs to and from the otherwise unused imaginary frequency components r=padenot (0ff120a534) - bug 1188704 remove now unused PerformInverseFFT() variation r=rillian (b82b4d459a) - bug 1188704 add accessor functions for setting frequency components for inverse FFT r=rillian (c8357b6268) - style (3b9fc23a34) - Bug 1173016 - Properly refcount the inner PeriodicWave object. r=karlt (634e09bb79) - bug 1187785 reverse tableInterpolationFactor to make it consistent with documentation r=rillian (928fa29f6c) - bug 1188704 redefine halfSize as fftSize / 2 r=rillian (1cf36a13e3) - bug 1188704 trim unnecessary extra basic waveform coeffient r=rillian (c33983684a) - bug 1188704 simplify culling of partials r=rillian (484932e54e) - bug 1188704 combine scaling with copying r=rillian (7e5d3f9c7d) - bug 1188704 limit number of Fourier coefficients used to halfSize earlier r=rillian (abba4f31bb) - bug 1188704 use existing FFTBlock arrays instead of allocating and copying and scaling r=rillian (74b58f3bc3) - warnings (cf6c0840e8) - Bug 1171436 - support at least 8192 elements for PeriodicWave r=padenot (7ddeb3ec3f) - bug 1221831 remove unused Reverb::reset() r=padenot (913849471f) - bug 1221831 remove unused Reverb::latencyFrames() r=padenot (767744e87c) - bug 1221830 use WEBAUDIO_BLOCK_SIZE constant in Reverb methods r=padenot (38dc6b0a92) - bug 1221831 use initial input buffer offset to control when convolver stages perform their FFT r=padenot (4f594ded7a) - bug 1221831 remove now-unnecessary m_preDelayBuffer r=padenot (af5b36efdc) - bug 1221830 use WEBAUDIO_BLOCK_SIZE slice size for background convolver thread r=padenot (0d6dbe1daa) - bug 1221830 double maximum realtime convolver stage size to 2048 r=padenot (1cf7698f2d) - bug 1221830 use WEBAUDIO_BLOCK_SIZE constant in FFTConvolver r=padenot (955e902db8) - bug 1221833 reduce FFTConvolver latency by one block r=padenot (024b13396a) - bug 1221833 replace initial direct convolution stage with fft r=padenot (70319e951b) - bug 1221833 remove first two half-block-size convolver stages r=padenot (2709910848) - bug 1221833 remove now-unused direct convolver r=padenot (d0cfb712af) - bug 1221836 return output pointer from FFTConvolver::process() to save a buffer copy r=padenot (4bf6c5e6c8) - Bug 1196608 - Link liblgpllibs against mozglue. r=mshal (3212491e68) - missing bit of 1157768 (3d6844b56e) - bug 1224102 reduce the size of the final FFT convolution stage if possible r=padenot (f1fbd7f04d) - Bug 1192587 - Build media/libav in unified mode. r=qDot (77dd2810e3) - Bug 1228230 - Rely more on top-level configure auto-detection. r=qdot (8add5f4014) - Bug 1261414 - Don't build libav with sysctl on Unix (it's not used anyway). r=glandium (195c3ddba4) - Bug 1222405 - (part 1) Store PeriodicWave components for later use r=padenot (a993b3303d) - Bug 1222405 - (part 2) Build band limited tables after fundamental frequency is known r=padenot (d350f2e201) - Bug 1222405 - (part 3) Build band limited tables lazily r=padenot (0dc6f013fc) --- accessible/windows/ia2/ia2Accessible.cpp | 28 +- accessible/windows/msaa/AccessibleWrap.cpp | 76 ++- accessible/windows/msaa/AccessibleWrap.h | 14 +- accessible/windows/msaa/DocAccessibleWrap.h | 18 + accessible/windows/msaa/IDSet.h | 126 +++++ accessible/windows/msaa/moz.build | 1 + b2g/confvars.sh | 1 - build/unix/uniq.py | 10 - config/external/lgpllibs/moz.build | 6 +- config/system-headers | 3 + configure.in | 238 ++++++---- .../tests/unit/test_DominatorTree_05.js | 68 +++ dom/camera/GonkRecorder.cpp | 163 +++++++ dom/camera/GonkRecorder.h | 12 +- dom/html/HTMLInputElement.cpp | 4 - .../core/nsIDOMNSEditableElement.idl | 6 +- .../security/nsIContentSecurityManager.idl | 2 +- dom/media/AbstractMediaDecoder.h | 89 +--- dom/media/EncodedBufferCache.h | 2 +- dom/media/FrameStatistics.h | 3 + dom/media/MediaDecoder.cpp | 106 ++++- dom/media/MediaDecoder.h | 109 +++-- dom/media/MediaDecoderOwner.h | 8 + dom/media/MediaDecoderReader.h | 15 +- dom/media/MediaDecoderStateMachine.cpp | 214 ++++----- dom/media/MediaDecoderStateMachine.h | 68 ++- dom/media/MediaFormatReader.cpp | 74 ++- dom/media/MediaFormatReader.h | 18 +- dom/media/MediaInfo.h | 3 + dom/media/MediaQueue.h | 8 +- dom/media/VideoFrameContainer.h | 2 + dom/media/android/AndroidMediaReader.h | 6 - .../compiledtest/TestAudioPacketizer.cpp | 1 - dom/media/directshow/DirectShowReader.cpp | 10 +- dom/media/directshow/DirectShowReader.h | 4 - dom/media/gstreamer/GStreamerReader.cpp | 28 +- dom/media/gstreamer/GStreamerReader.h | 3 +- dom/media/mediasink/DecodedStream.cpp | 87 ++-- dom/media/mediasink/DecodedStream.h | 9 - dom/media/mediasink/VideoSink.cpp | 28 +- dom/media/mediasink/VideoSink.h | 9 +- dom/media/ogg/OggReader.cpp | 17 +- dom/media/ogg/OggReader.h | 2 - dom/media/omx/MediaOmxCommonDecoder.cpp | 4 - dom/media/omx/MediaOmxReader.cpp | 9 +- dom/media/omx/MediaOmxReader.h | 2 - dom/media/raw/RawReader.cpp | 7 - dom/media/raw/RawReader.h | 2 - dom/media/wave/WaveReader.cpp | 7 - dom/media/wave/WaveReader.h | 2 - dom/media/webaudio/AudioContext.cpp | 3 +- dom/media/webaudio/BufferDecoder.cpp | 42 -- dom/media/webaudio/BufferDecoder.h | 18 - dom/media/webaudio/ConvolverNode.cpp | 3 +- dom/media/webaudio/FFTBlock.h | 112 +++-- dom/media/webaudio/PeriodicWave.cpp | 1 - dom/media/webaudio/blink/Biquad.cpp | 7 +- dom/media/webaudio/blink/DenormalDisabler.h | 1 - dom/media/webaudio/blink/DirectConvolver.cpp | 347 -------------- dom/media/webaudio/blink/DirectConvolver.h | 61 --- .../blink/DynamicsCompressorKernel.cpp | 2 +- dom/media/webaudio/blink/FFTConvolver.cpp | 87 ++-- dom/media/webaudio/blink/FFTConvolver.h | 23 +- dom/media/webaudio/blink/HRTFElevation.cpp | 16 +- dom/media/webaudio/blink/HRTFPanner.cpp | 52 ++- dom/media/webaudio/blink/PeriodicWave.cpp | 247 +++++----- dom/media/webaudio/blink/PeriodicWave.h | 30 +- dom/media/webaudio/blink/Reverb.cpp | 55 +-- dom/media/webaudio/blink/Reverb.h | 14 +- .../blink/ReverbAccumulationBuffer.cpp | 2 +- .../webaudio/blink/ReverbAccumulationBuffer.h | 2 +- dom/media/webaudio/blink/ReverbConvolver.cpp | 129 +++--- dom/media/webaudio/blink/ReverbConvolver.h | 19 +- .../webaudio/blink/ReverbConvolverStage.cpp | 144 ++---- .../webaudio/blink/ReverbConvolverStage.h | 20 +- dom/media/webaudio/blink/moz.build | 4 - .../webaudio/test/test_periodicWave.html | 4 +- dom/media/webm/WebMReader.cpp | 8 +- dom/media/webm/WebMReader.h | 2 - dom/svg/SVGContentUtils.h | 1 - dom/system/gonk/GeolocationUtil.cpp | 28 ++ dom/system/gonk/GeolocationUtil.h | 13 + .../gonk/GonkGPSGeolocationProvider.cpp | 153 ++---- dom/system/gonk/moz.build | 16 +- dom/system/gonk/mozstumbler/MozStumbler.cpp | 99 +++- dom/system/gonk/mozstumbler/MozStumbler.h | 2 + dom/webidl/DominatorTree.webidl | 8 + extensions/moz.build | 2 +- gfx/2d/Blur.cpp | 1 - gfx/2d/DrawCommand.h | 3 - gfx/2d/DrawTargetD2D.cpp | 2 +- gfx/2d/FilterNodeSoftware.cpp | 2 - gfx/2d/PathD2D.cpp | 1 - gfx/2d/PathHelpers.cpp | 4 - gfx/2d/PathHelpers.h | 1 - gfx/2d/image_operations.cpp | 1 - gfx/2d/moz.build | 5 +- gfx/layers/apz/src/AsyncPanZoomController.cpp | 1 - gfx/thebes/gfxContext.cpp | 3 - gfx/thebes/gfxPrefs.h | 2 +- js/public/TracingAPI.h | 1 + js/public/TrackedOptimizationInfo.h | 3 + js/public/UbiNodeDominatorTree.h | 5 + js/src/asmjs/AsmJSModule.cpp | 18 +- js/src/builtin/MapObject.cpp | 49 +- js/src/builtin/MapObject.h | 4 +- js/src/builtin/ModuleObject.cpp | 1 + js/src/builtin/Object.cpp | 7 +- js/src/builtin/RegExp.cpp | 52 ++- js/src/builtin/TestingFunctions.cpp | 4 +- js/src/builtin/WeakMapObject.cpp | 3 +- js/src/builtin/WeakSetObject.cpp | 23 +- js/src/builtin/WeakSetObject.h | 2 +- js/src/configure.in | 12 +- js/src/ctypes/CTypes.cpp | 12 +- js/src/frontend/BytecodeEmitter.cpp | 4 +- js/src/gc/Marking.cpp | 16 + js/src/gc/Tracer.h | 8 + js/src/gc/Zone.h | 11 +- .../jit-test/tests/TypedObject/bug976697.js | 15 - js/src/jit-test/tests/arguments/bug1227287.js | 6 + .../baseline/arraySubclassPropertyLookup.js | 17 + .../tests/basic/iterable-error-messages.js | 39 ++ .../ion/inline-Math-random-before-called.js | 11 + js/src/jit-test/tests/xdr/classes.js | 11 + js/src/jit/Bailouts.cpp | 2 +- js/src/jit/BaselineBailouts.cpp | 2 +- js/src/jit/BaselineCompiler.cpp | 6 + js/src/jit/BaselineCompiler.h | 1 + js/src/jit/BaselineIC.cpp | 8 +- js/src/jit/BaselineJIT.cpp | 2 +- js/src/jit/CodeGenerator.cpp | 2 +- js/src/jit/Ion.cpp | 12 +- js/src/jit/Ion.h | 2 +- js/src/jit/IonAnalysis.cpp | 4 +- js/src/jit/IonBuilder.cpp | 59 ++- js/src/jit/IonBuilder.h | 4 +- js/src/jit/IonOptimizationLevels.cpp | 6 +- js/src/jit/IonOptimizationLevels.h | 38 +- js/src/jit/JitFrames.cpp | 4 +- js/src/jit/JitOptions.cpp | 18 +- js/src/jit/JitOptions.h | 6 +- js/src/jit/Lowering.cpp | 2 +- js/src/jit/MCallOptimize.cpp | 8 +- js/src/jit/RangeAnalysis.cpp | 6 +- js/src/jit/shared/CodeGenerator-shared.cpp | 2 +- js/src/jit/shared/CodeGenerator-shared.h | 2 +- js/src/jit/shared/Lowering-shared-inl.h | 2 +- js/src/jsapi.cpp | 26 +- js/src/jsarray.cpp | 56 +-- js/src/jsarray.h | 5 +- js/src/jsatom.cpp | 16 +- js/src/jsatom.h | 9 +- js/src/jsbool.cpp | 8 +- js/src/jscompartment.cpp | 7 +- js/src/jsdate.cpp | 13 +- js/src/jsdate.h | 2 +- js/src/jsexn.cpp | 7 +- js/src/jsfriendapi.cpp | 20 +- js/src/jsfriendapi.h | 40 +- js/src/jsfun.cpp | 42 +- js/src/jsfun.h | 12 +- js/src/jsgc.cpp | 14 +- js/src/jsmath.cpp | 1 - js/src/jsmath.h | 27 +- js/src/jsnum.cpp | 6 +- js/src/jsobj.cpp | 41 +- js/src/jsobj.h | 32 +- js/src/jsobjinlines.h | 18 + js/src/jsopcode.cpp | 3 +- js/src/jsopcode.h | 2 +- js/src/jsprf.cpp | 21 +- js/src/jsscript.cpp | 22 +- js/src/jsscript.h | 3 +- js/src/jsstr.cpp | 9 +- js/src/make-source-package.sh | 135 ++++-- js/src/shell/js.cpp | 72 +-- .../ecma_6/Class/boundFunctionSubclassing.js | 24 + .../ecma_6/Class/bytecodePatternMatching.js | 34 ++ .../ecma_6/Class/extendBuiltinConstructors.js | 112 +++++ .../ecma_6/Class/subclassedArrayUnboxed.js | 29 ++ .../ecma_6/Class/superCallBaseInvoked.js | 2 - .../DataView/detach-after-construction.js | 13 + js/src/tests/ecma_6/Map/iterable.js | 28 ++ js/src/tests/ecma_6/Reflect/construct.js | 4 +- .../ecma_6/RegExp/constructor-ordering-2.js | 21 + .../ecma_6/RegExp/constructor-ordering.js | 16 + .../TypedArray/constructor-non-detached.js | 29 ++ .../tests/js1_5/Error/constructor-ordering.js | 17 + .../js1_8_5/extensions/decompile-for-of.js | 14 +- js/src/vm/ArgumentsObject.cpp | 4 +- js/src/vm/ArrayBufferObject.cpp | 58 ++- js/src/vm/ArrayBufferObject.h | 24 +- js/src/vm/BooleanObject-inl.h | 9 +- js/src/vm/BooleanObject.h | 7 +- js/src/vm/Debugger.cpp | 46 +- js/src/vm/ErrorObject.cpp | 12 +- js/src/vm/ErrorObject.h | 2 +- js/src/vm/ForOfIterator.cpp | 6 +- js/src/vm/Interpreter.cpp | 24 +- js/src/vm/NumberObject-inl.h | 9 +- js/src/vm/NumberObject.h | 7 +- js/src/vm/ObjectGroup.cpp | 90 ++-- js/src/vm/ObjectGroup.h | 5 +- js/src/vm/Opcodes.h | 11 +- js/src/vm/RegExpObject.cpp | 31 +- js/src/vm/RegExpObject.h | 13 +- js/src/vm/SelfHosting.cpp | 17 +- js/src/vm/StringObject-inl.h | 4 +- js/src/vm/StringObject.h | 1 + js/src/vm/TypedArrayCommon.h | 6 + js/src/vm/TypedArrayObject.cpp | 302 ++++++++---- js/src/vm/TypedArrayObject.h | 19 +- js/src/vm/UnboxedObject.cpp | 2 +- js/src/vm/Xdr.h | 2 +- js/xpconnect/src/XPCJSRuntime.cpp | 17 +- js/xpconnect/src/xpcprivate.h | 2 +- js/xpconnect/tests/chrome/chrome.ini | 1 + .../chrome/test_windowProxyDeadWrapper.html | 72 +++ js/xpconnect/wrappers/WrapperFactory.cpp | 4 + layout/svg/nsSVGUtils.h | 2 - media/libav/config_darwin.asm | 4 - media/libav/config_darwin.h | 4 - media/libav/config_unix.asm | 4 - media/libav/config_unix.h | 6 +- media/libav/config_win.asm | 4 - media/libav/config_win.h | 4 - media/libav/libavutil/internal.h | 2 +- media/libav/moz.build | 15 +- media/libcubeb/AUTHORS | 2 + media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/include/cubeb.h | 109 +++++ media/libcubeb/src/cubeb-internal.h | 5 +- media/libcubeb/src/cubeb.c | 60 ++- media/libcubeb/src/cubeb_alsa.c | 3 +- media/libcubeb/src/cubeb_audiotrack.c | 1 + media/libcubeb/src/cubeb_audiounit.c | 296 +++++++++++- media/libcubeb/src/cubeb_opensl.c | 1 + media/libcubeb/src/cubeb_pulse.c | 214 ++++++++- media/libcubeb/src/cubeb_sndio.c | 1 + media/libcubeb/src/cubeb_wasapi.cpp | 435 ++++++++++++++---- media/libcubeb/src/cubeb_winmm.c | 329 ++++++++++++- media/libcubeb/src/moz.build | 2 + media/libcubeb/tests/test_audio.cpp | 2 + media/libstagefright/binding/BufferStream.cpp | 2 +- .../media/libstagefright/SampleIterator.cpp | 13 +- mfbt/Assertions.h | 27 +- mfbt/Constants.h | 16 - mfbt/moz.build | 1 - moz.build | 3 + toolkit/devtools/server/DominatorTree.cpp | 94 +++- toolkit/devtools/server/DominatorTree.h | 4 + .../tests/unit/test_DominatorTree_05.js | 68 +++ widget/windows/nsWinGesture.cpp | 3 +- xpcom/ds/nsMathUtils.h | 2 - 255 files changed, 5079 insertions(+), 2547 deletions(-) create mode 100644 accessible/windows/msaa/IDSet.h delete mode 100755 build/unix/uniq.py create mode 100644 devtools/shared/heapsnapshot/tests/unit/test_DominatorTree_05.js delete mode 100644 dom/media/webaudio/blink/DirectConvolver.cpp delete mode 100644 dom/media/webaudio/blink/DirectConvolver.h create mode 100644 dom/system/gonk/GeolocationUtil.cpp create mode 100644 dom/system/gonk/GeolocationUtil.h delete mode 100644 js/src/jit-test/tests/TypedObject/bug976697.js create mode 100644 js/src/jit-test/tests/arguments/bug1227287.js create mode 100644 js/src/jit-test/tests/baseline/arraySubclassPropertyLookup.js create mode 100644 js/src/jit-test/tests/basic/iterable-error-messages.js create mode 100644 js/src/jit-test/tests/ion/inline-Math-random-before-called.js create mode 100644 js/src/jit-test/tests/xdr/classes.js create mode 100644 js/src/tests/ecma_6/Class/boundFunctionSubclassing.js create mode 100644 js/src/tests/ecma_6/Class/bytecodePatternMatching.js create mode 100644 js/src/tests/ecma_6/Class/extendBuiltinConstructors.js create mode 100644 js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js create mode 100644 js/src/tests/ecma_6/DataView/detach-after-construction.js create mode 100644 js/src/tests/ecma_6/Map/iterable.js create mode 100644 js/src/tests/ecma_6/RegExp/constructor-ordering-2.js create mode 100644 js/src/tests/ecma_6/RegExp/constructor-ordering.js create mode 100644 js/src/tests/ecma_6/TypedArray/constructor-non-detached.js create mode 100644 js/src/tests/js1_5/Error/constructor-ordering.js create mode 100644 js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html delete mode 100644 mfbt/Constants.h create mode 100644 toolkit/devtools/server/tests/unit/test_DominatorTree_05.js diff --git a/accessible/windows/ia2/ia2Accessible.cpp b/accessible/windows/ia2/ia2Accessible.cpp index c231125518..f9672dc553 100644 --- a/accessible/windows/ia2/ia2Accessible.cpp +++ b/accessible/windows/ia2/ia2Accessible.cpp @@ -18,6 +18,7 @@ #include "nsIAccessibleTypes.h" #include "mozilla/a11y/PDocAccessible.h" #include "Relation.h" +#include "nsAccessibilityService.h" #include "nsIPersistentProperties2.h" #include "nsISimpleEnumerator.h" @@ -364,7 +365,7 @@ ia2Accessible::get_states(AccessibleStates* aStates) AccessibleWrap* acc = static_cast(this); if (acc->IsDefunct()) { *aStates = IA2_STATE_DEFUNCT; - return CO_E_OBJNOTCONNECTED; + return S_OK; } uint64_t state; @@ -505,7 +506,7 @@ ia2Accessible::get_uniqueID(long* aUniqueID) return E_INVALIDARG; AccessibleWrap* acc = static_cast(this); - *aUniqueID = - reinterpret_cast(acc->UniqueID()); + *aUniqueID = AccessibleWrap::GetChildIDFor(acc); return S_OK; A11Y_TRYBLOCK_END @@ -661,7 +662,28 @@ ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible, *aAccessible = nullptr; *aCaretOffset = -1; - return E_NOTIMPL; + + AccessibleWrap* acc = static_cast(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + int32_t caretOffset = -1; + Accessible* accWithCaret = SelectionMgr()->AccessibleWithCaret(&caretOffset); + if (acc->Document() != accWithCaret->Document()) + return S_FALSE; + + Accessible* child = accWithCaret; + while (!child->IsDoc() && child != acc) + child = child->Parent(); + + if (child != acc) + return S_FALSE; + + *aAccessible = static_cast( + static_cast(accWithCaret)); + (*aAccessible)->AddRef(); + *aCaretOffset = caretOffset; + return S_OK; A11Y_TRYBLOCK_END } diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index 0155ab6502..dcf7615139 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -56,16 +56,48 @@ const uint32_t USE_ROLE_STRING = 0; static gAccessibles = 0; #endif +#ifdef _WIN64 +IDSet AccessibleWrap::sIDGen; + +static const uint32_t kNoID = 0; +#endif + static const int32_t kIEnumVariantDisconnected = -1; //////////////////////////////////////////////////////////////////////////////// // AccessibleWrap //////////////////////////////////////////////////////////////////////////////// +AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + Accessible(aContent, aDoc) +#ifdef _WIN64 + , mID(kNoID) +#endif +{ +} + +AccessibleWrap::~AccessibleWrap() +{ +#ifdef _WIN64 + if (mID != kNoID) + sIDGen.ReleaseID(mID); +#endif +} ITypeInfo* AccessibleWrap::gTypeInfo = nullptr; NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible) +void +AccessibleWrap::Shutdown() +{ +#ifdef _WIN64 + if (mID != kNoID) + static_cast(mDoc)->RemoveID(mID); +#endif + + Accessible::Shutdown(); +} + //----------------------------------------------------- // IUnknown interface methods - see iunknown.h for documentation //----------------------------------------------------- @@ -1255,10 +1287,23 @@ AccessibleWrap::GetChildIDFor(Accessible* aAccessible) // so that the 3rd party application can call back and get the IAccessible // the event occurred on. - // Yes, this means we're only compatibible with 32 bit - // MSAA is only available for 32 bit windows, so it's okay - // XXX: bug 606080 - return aAccessible ? - NS_PTR_TO_INT32(aAccessible->UniqueID()) : 0; +#ifdef _WIN64 + if (!aAccessible || !aAccessible->Document()) + return 0; + + uint32_t* id = & static_cast(aAccessible)->mID; + if (*id != kNoID) + return *id; + + *id = sIDGen.GetID(); + DocAccessibleWrap* doc = + static_cast(aAccessible->Document()); + doc->AddID(*id, static_cast(aAccessible)); + + return *id; +#else + return - reinterpret_cast(aAccessible); +#endif } HWND @@ -1308,6 +1353,25 @@ AccessibleWrap::NativeAccessible(Accessible* aAccessible) return static_cast(msaaAccessible); } +#ifdef _WIN64 +static Accessible* +GetAccessibleInSubtree(DocAccessible* aDoc, uint32_t aID) +{ + Accessible* child = static_cast(aDoc)->GetAccessibleByID(aID); + if (child) + return child; + + uint32_t childDocCount = aDoc->ChildDocumentCount(); + for (uint32_t i = 0; i < childDocCount; i++) { + child = GetAccessibleInSubtree(aDoc->GetChildDocumentAt(i), aID); + if (child) + return child; + } + + return nullptr; + } +#endif + Accessible* AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild) { @@ -1343,7 +1407,11 @@ AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild) DocAccessible* document = Document(); Accessible* child = +#ifdef _WIN64 + GetAccessibleInSubtree(document, static_cast(aVarChild.lVal)); +#else document->GetAccessibleByUniqueIDInSubtree(uniqueID); +#endif // If it is a document then just return an accessible. if (IsDoc()) diff --git a/accessible/windows/msaa/AccessibleWrap.h b/accessible/windows/msaa/AccessibleWrap.h index f759a503e4..05ce578910 100644 --- a/accessible/windows/msaa/AccessibleWrap.h +++ b/accessible/windows/msaa/AccessibleWrap.h @@ -15,6 +15,7 @@ #include "ia2AccessibleHyperlink.h" #include "ia2AccessibleValue.h" #include "mozilla/a11y/ProxyAccessible.h" +#include "mozilla/a11y/IDSet.h" #ifdef __GNUC__ // Inheriting from both XPCOM and MSCOM interfaces causes a lot of warnings @@ -33,8 +34,7 @@ class AccessibleWrap : public Accessible, public ia2AccessibleValue { public: // construction, destruction - AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : - Accessible(aContent, aDoc) { } + AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc); // nsISupports NS_DECL_ISUPPORTS_INHERITED @@ -152,6 +152,7 @@ public: // construction, destruction // Accessible virtual nsresult HandleAccEvent(AccEvent* aEvent); + virtual void Shutdown() override; // Helper methods static int32_t GetChildIDFor(Accessible* aAccessible); @@ -176,7 +177,11 @@ public: // construction, destruction static IDispatch* NativeAccessible(Accessible* aAccessible); protected: - virtual ~AccessibleWrap() { } + virtual ~AccessibleWrap(); + +#ifdef _WIN64 + uint32_t mID; +#endif /** * Creates ITypeInfo for LIBID_Accessibility if it's needed and returns it. @@ -185,6 +190,9 @@ protected: static ITypeInfo* gTypeInfo; +#ifdef _WIN64 + static IDSet sIDGen; +#endif enum navRelations { NAVRELATION_CONTROLLED_BY = 0x1000, diff --git a/accessible/windows/msaa/DocAccessibleWrap.h b/accessible/windows/msaa/DocAccessibleWrap.h index 80f2f41ab8..18e58b0ab3 100644 --- a/accessible/windows/msaa/DocAccessibleWrap.h +++ b/accessible/windows/msaa/DocAccessibleWrap.h @@ -34,12 +34,30 @@ public: // DocAccessible virtual void* GetNativeWindow() const; + /** + * Manage the mapping from id to Accessible. + */ +#ifdef _WIN64 + void AddID(uint32_t aID, AccessibleWrap* aAcc) + { mIDToAccessibleMap.Put(aID, aAcc); } + void RemoveID(uint32_t aID) { mIDToAccessibleMap.Remove(aID); } + AccessibleWrap* GetAccessibleByID(uint32_t aID) const + { return mIDToAccessibleMap.Get(aID); } +#endif + protected: // DocAccessible virtual void DoInitialUpdate(); protected: void* mHWND; + + /* + * This provides a mapping from 32 bit id to accessible objects. + */ +#ifdef _WIN64 + nsDataHashtable mIDToAccessibleMap; +#endif }; } // namespace a11y diff --git a/accessible/windows/msaa/IDSet.h b/accessible/windows/msaa/IDSet.h new file mode 100644 index 0000000000..4ab1402854 --- /dev/null +++ b/accessible/windows/msaa/IDSet.h @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * A class to generate unique IDs in the range [ - 2^31, 0 ) + */ + +#ifndef MOZILLA_A11Y_IDSet_h_ +#define MOZILLA_A11Y_IDSet_h_ + +#include "mozilla/Attributes.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/SplayTree.h" + +namespace mozilla { +namespace a11y { + +/** + * On windows an accessible's id must be a negative 32 bit integer. It is + * important to support recycling arbitrary IDs because accessibles can be + * created and destroyed at any time in the life of a page. IDSet provides 2 + * operations: generate an ID in the range [ - 2^31, 0 ), and release an ID so + * it can be allocated again. Allocated ID are tracked by a sparse bitmap + * implemented with a splay tree. Nodes in the tree are keyed by the upper N + * bits of the bitwise negation of the ID, and the node contains a bitmap + * tracking the allocation of 2^(32 - N) IDs. + */ +class IDSet +{ +public: + MOZ_CONSTEXPR IDSet() : mBitSet(), mIdx(0) {} + + /** + * Return a new unique id. + */ + uint32_t GetID() + { + uint32_t idx = mIdx; + while (true) { + BitSetElt* elt = mBitSet.findOrInsert(BitSetElt(idx)); + if (elt->mBitvec[0] != UINT64_MAX) { + uint32_t i = CountTrailingZeroes64(~elt->mBitvec[0]); + + elt->mBitvec[0] |= (1ull << i); + mIdx = idx; + return ~(elt->mIdx * bitsPerElt + i); + } + + if (elt->mBitvec[1] != UINT64_MAX) { + uint32_t i = CountTrailingZeroes64(~elt->mBitvec[1]); + + elt->mBitvec[1] |= (1ull << i); + mIdx = idx; + return ~(elt->mIdx * bitsPerElt + bitsPerWord + i); + } + + idx++; + if (idx > sMaxIdx) { + idx = 0; + } + + if (idx == mIdx) { + MOZ_CRASH("used up all the available ids"); + } + } + } + + /** + * Free a no longer required id so it may be allocated again. + */ + void ReleaseID(uint32_t aID) + { + aID = ~aID; + MOZ_ASSERT(aID < static_cast(INT32_MAX)); + + uint32_t idx = aID / bitsPerElt; + mIdx = idx; + BitSetElt* elt = mBitSet.find(BitSetElt(idx)); + MOZ_ASSERT(elt); + + uint32_t vecIdx = (aID % bitsPerElt) / bitsPerWord; + elt->mBitvec[vecIdx] &= ~(1ull << (aID % bitsPerWord)); + if (elt->mBitvec[0] == 0 && elt->mBitvec[1] == 0) { + delete mBitSet.remove(*elt); + } + } + +private: + static const unsigned int wordsPerElt = 2; + static const unsigned int bitsPerWord = 64; + static const unsigned int bitsPerElt = wordsPerElt * bitsPerWord; + static const uint32_t sMaxIdx = INT32_MAX / bitsPerElt; + + struct BitSetElt : mozilla::SplayTreeNode + { + explicit BitSetElt(uint32_t aIdx) : + mIdx(aIdx) + { mBitvec[0] = mBitvec[1] = 0; } + + uint64_t mBitvec[wordsPerElt]; + uint32_t mIdx; + + static int compare(const BitSetElt& a, const BitSetElt& b) + { + if (a.mIdx == b.mIdx) { + return 0; + } + + if (a.mIdx < b.mIdx) { + return -1; + } + return 1; + } + }; + + SplayTree mBitSet; + uint32_t mIdx; +}; + +} +} + +#endif diff --git a/accessible/windows/msaa/moz.build b/accessible/windows/msaa/moz.build index ad8169f4f6..42469ada2d 100644 --- a/accessible/windows/msaa/moz.build +++ b/accessible/windows/msaa/moz.build @@ -12,6 +12,7 @@ EXPORTS.mozilla.a11y += [ 'AccessibleWrap.h', 'Compatibility.h', 'HyperTextAccessibleWrap.h', + 'IDSet.h', ] UNIFIED_SOURCES += [ diff --git a/b2g/confvars.sh b/b2g/confvars.sh index 8c9e4c1ab4..6546e8fed8 100755 --- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -23,7 +23,6 @@ MOZ_SERVICES_METRICS=1 MOZ_WEBSMS_BACKEND=1 MOZ_NO_SMART_CARDS=1 MOZ_APP_STATIC_INI=1 -NSS_NO_LIBPKIX=1 NSS_DISABLE_DBM=1 MOZ_NO_EV_CERTS=1 MOZ_DISABLE_EXPORT_JS=1 diff --git a/build/unix/uniq.py b/build/unix/uniq.py deleted file mode 100755 index e3afbbaf0f..0000000000 --- a/build/unix/uniq.py +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env python -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -'''Prints the given arguments in sorted order with duplicates removed.''' - -import sys - -print(' '.join(sorted(set(sys.argv[1:])))) diff --git a/config/external/lgpllibs/moz.build b/config/external/lgpllibs/moz.build index b1cc9420fb..27df35dbf8 100644 --- a/config/external/lgpllibs/moz.build +++ b/config/external/lgpllibs/moz.build @@ -10,5 +10,9 @@ # # Any library added here should also be reflected in the about:license page. -SharedLibrary('lgpllibs') +GeckoSharedLibrary('lgpllibs', linkage=None) SHARED_LIBRARY_NAME = 'lgpllibs' + +if CONFIG['MOZ_LIBAV_FFT']: + DIRS += ['/media/libav'] + DEFFILE = SRCDIR + '/lgpllibs.def' diff --git a/config/system-headers b/config/system-headers index e3fabec8ef..164207f815 100644 --- a/config/system-headers +++ b/config/system-headers @@ -183,6 +183,7 @@ app/Message.h app/MessageRunner.h arpa/inet.h arpa/nameser.h +array asm/page.h asm/sigcontext.h asm/signal.h @@ -752,6 +753,7 @@ media/stagefright/foundation/hexdump.h media/stagefright/MediaBuffer.h media/stagefright/MediaBufferGroup.h media/stagefright/MediaCodec.h +media/stagefright/MediaCodecSource.h media/stagefright/MediaDefs.h media/stagefright/MediaErrors.h media/stagefright/MediaExtractor.h @@ -1352,6 +1354,7 @@ xlocale.h #ifdef MOZ_SHARED_ICU unicode/locid.h unicode/numsys.h +unicode/timezone.h unicode/ucal.h unicode/uchar.h unicode/uclean.h diff --git a/configure.in b/configure.in index 72fa6e460b..8931367433 100644 --- a/configure.in +++ b/configure.in @@ -338,6 +338,7 @@ if test -n "$gonkdir" ; then AC_DEFINE(HAVE_PTHREADS) MOZ_CHROME_FILE_FORMAT=omni direct_nspr_config=1 + android_cxx_stl=mozstlport else MOZ_ANDROID_NDK @@ -522,6 +523,7 @@ case "$target" in AC_DEFINE(_CRT_SECURE_NO_WARNINGS) AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) + AC_DEFINE(_USE_MATH_DEFINES) # Otherwise MSVC's math.h doesn't #define M_PI. if test "$_CC_MAJOR_VERSION" = "18" -a "$_CC_BUILD_VERSION" -ge "30723"; then _CC_SUITE=12 @@ -652,7 +654,7 @@ See https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) fi if test "$WRAP_STL_INCLUDES" = "1"; then - STL_FLAGS='-I$(DIST)/stl_wrappers' + STL_FLAGS="-I${DIST}/stl_wrappers" fi CFLAGS="$CFLAGS -D_HAS_EXCEPTIONS=0" CXXFLAGS="$CXXFLAGS -D_HAS_EXCEPTIONS=0" @@ -2530,7 +2532,7 @@ if test "$GNU_CC" -a "$OS_TARGET" != WINNT; then VISIBILITY_FLAGS='-fvisibility=hidden -fvisibility-inlines-hidden' ;; *) - VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(MOZILLA_DIR)/config/gcc_hidden.h' + VISIBILITY_FLAGS="-I${DIST}/system_wrappers -include ${_topsrcdir}/config/gcc_hidden.h" WRAP_SYSTEM_INCLUDES=1 ;; esac @@ -2545,13 +2547,13 @@ case "${OS_TARGET}" in WINNT|Darwin|Android) ;; *) - STL_FLAGS='-I$(DIST)/stl_wrappers' + STL_FLAGS="-I${DIST}/stl_wrappers" WRAP_STL_INCLUDES=1 ;; esac AC_SUBST(WRAP_SYSTEM_INCLUDES) -AC_SUBST(VISIBILITY_FLAGS) +AC_SUBST_LIST(VISIBILITY_FLAGS) dnl Checks for header files. dnl ======================================================== @@ -2638,14 +2640,6 @@ if test "x$enable_dtrace" = "xyes"; then fi AC_SUBST(HAVE_DTRACE) -case $target in -*-aix4.3*|*-aix5*) - ;; -*) - MOZ_CHECK_HEADERS(sys/cdefs.h) - ;; -esac - MOZ_LINUX_PERF_EVENT dnl Checks for libraries. @@ -3649,7 +3643,7 @@ MOZ_DISABLE_PARENTAL_CONTROLS=1 ENABLE_SYSTEM_EXTENSION_DIRS=1 MOZ_BRANDING_DIRECTORY= MOZ_OFFICIAL_BRANDING= -MC_OFFICIAL=1 +MC_OFFICIAL=1 MOZ_FEEDS=1 MOZ_AUTH_EXTENSION=1 MOZ_RAW= @@ -3733,11 +3727,9 @@ MOZ_TIME_MANAGER= MOZ_SIMPLEPUSH= MOZ_PAY= MOZ_AUDIO_CHANNEL_MANAGER= -NSS_NO_LIBPKIX= MOZ_CONTENT_SANDBOX= MOZ_GMP_SANDBOX= MOZ_SANDBOX= -MOZ_DEVTOOLS= case "$target_os" in mingw*) @@ -3917,8 +3909,6 @@ fi case "$MOZ_BUILD_APP" in browser) AC_DEFINE(MOZ_PHOENIX) - - BUILD_BACKENDS="$BUILD_BACKENDS FasterMake" ;; xulrunner) @@ -3933,6 +3923,8 @@ b2g/dev) ;; esac +BUILD_BACKENDS="$BUILD_BACKENDS FasterMake" + if test -n "$MOZ_B2GDROID"; then AC_DEFINE(MOZ_B2GDROID) fi @@ -4421,42 +4413,42 @@ MOZ_ARG_ENABLE_STRING(ui-locale, MOZ_UI_LOCALE=$enableval ) AC_SUBST(MOZ_UI_LOCALE) -dnl ======================================================== -dnl = Vendor override -dnl ======================================================== -MOZ_ARG_DISABLE_BOOL(official-vendor, -[ --disable-official-vendor Disable the use of official vendor.], - MC_OFFICIAL=, - MC_OFFICIAL=1) - -dnl ======================================================== -dnl = Trademarked Branding -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(official-branding, -[ --enable-official-branding - Enable official branding - Do not distribute builds with - --enable-official-branding unless you have - permission to use the name/logo per - http://www.palemoon.org/redist.shtml .], -[ +dnl ======================================================== +dnl = Vendor override +dnl ======================================================== +MOZ_ARG_DISABLE_BOOL(official-vendor, +[ --disable-official-vendor Disable the use of official vendor.], + MC_OFFICIAL=, + MC_OFFICIAL=1) + +dnl ======================================================== +dnl = Trademarked Branding +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(official-branding, +[ --enable-official-branding + Enable official branding + Do not distribute builds with + --enable-official-branding unless you have + permission to use the name/logo per + http://www.palemoon.org/redist.shtml .], +[ if test -z "$MOZ_OFFICIAL_BRANDING_DIRECTORY"; then AC_MSG_ERROR([You must specify MOZ_OFFICIAL_BRANDING_DIRECTORY to use --enable-official-branding.]) else MOZ_BRANDING_DIRECTORY=${MOZ_OFFICIAL_BRANDING_DIRECTORY} - MOZ_OFFICIAL_BRANDING=1 - MC_OFFICIAL=1 + MOZ_OFFICIAL_BRANDING=1 + MC_OFFICIAL=1 fi -], MOZ_OFFICIAL_BRANDING=) - -AC_SUBST(MC_OFFICIAL) -if test -n "$MC_OFFICIAL"; then - AC_DEFINE(MC_OFFICIAL) -fi - -AC_SUBST(MOZ_OFFICIAL_BRANDING) -if test -n "$MOZ_OFFICIAL_BRANDING"; then - AC_DEFINE(MOZ_OFFICIAL_BRANDING) +], MOZ_OFFICIAL_BRANDING=) + +AC_SUBST(MC_OFFICIAL) +if test -n "$MC_OFFICIAL"; then + AC_DEFINE(MC_OFFICIAL) +fi + +AC_SUBST(MOZ_OFFICIAL_BRANDING) +if test -n "$MOZ_OFFICIAL_BRANDING"; then + AC_DEFINE(MOZ_OFFICIAL_BRANDING) fi MOZ_ARG_WITH_STRING(branding, @@ -4476,22 +4468,22 @@ fi AC_SUBST(MOZ_BRANDING_DIRECTORY) -dnl ======================================================== -dnl = Private Build -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(private-build, -[ --enable-private-build Enable private builds - This allows you to build with official - branding for personal use only using any - build time configuration.], - MC_PRIVATE_BUILD=1, - MC_PRIVATE_BUILD=) - -AC_SUBST(MC_PRIVATE_BUILD) -if test -n "$MC_PRIVATE_BUILD"; then - AC_DEFINE(MC_PRIVATE_BUILD) -fi - +dnl ======================================================== +dnl = Private Build +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(private-build, +[ --enable-private-build Enable private builds + This allows you to build with official + branding for personal use only using any + build time configuration.], + MC_PRIVATE_BUILD=1, + MC_PRIVATE_BUILD=) + +AC_SUBST(MC_PRIVATE_BUILD) +if test -n "$MC_PRIVATE_BUILD"; then + AC_DEFINE(MC_PRIVATE_BUILD) +fi + dnl ======================================================== dnl = Distribution ID dnl ======================================================== @@ -4577,7 +4569,7 @@ then MOZ_ARG_DISABLE_BOOL(gconf, [ --disable-gconf Disable Gconf support ], MOZ_ENABLE_GCONF=, - MOZ_ENABLE_GCONF=force) + MOZ_ENABLE_GCONF=1) if test "$MOZ_ENABLE_GCONF" then @@ -4585,11 +4577,12 @@ then MOZ_GCONF_LIBS=`$PKG_CONFIG --libs gobject-2.0` MOZ_ENABLE_GCONF=1 ],[ - if test "$MOZ_ENABLE_GCONF" = "force" + if test -n "$MOZ_ENABLE_GCONF"; then AC_MSG_ERROR([* * * Could not find gconf-2.0 ]) + else + AC_MSG_WARN([Many automated tests will fail with --disable-gconf. See bug 1167201.]) fi - MOZ_ENABLE_GCONF= ]) fi @@ -5993,6 +5986,74 @@ elif test -n "$MOZ_LIBJPEG_TURBO"; then AC_MSG_WARN([No assembler or assembly support for libjpeg-turbo. Using unoptimized C routines.]) fi +dnl ======================================================== +dnl = libav-fft configuration +dnl ======================================================== + +MOZ_LIBAV_FFT= + +dnl Turn on libav-fft for 32-bit windows, and all 64-bit supported platforms. +dnl 32-bit linux/os x have text relocation issues. + +case "$OS_ARCH:$CPU_ARCH" in + WINNT:x86) + MOZ_LIBAV_FFT=1 + ;; + *:x86_64) + MOZ_LIBAV_FFT=1 + ;; +esac + +dnl Detect if we can use yasm to compile libav's assembly + +if test -n "$MOZ_LIBAV_FFT"; then + AC_DEFINE(MOZ_LIBAV_FFT) + dnl Do we support libav-fft on this platform? + case "$OS_ARCH:$CPU_ARCH" in + Darwin:x86_64) + LIBAV_FFT_ASFLAGS="-f macho64 -rnasm -pnasm -D__x86_64__ -DPIC -DMACHO" + ;; + WINNT:x86) + LIBAV_FFT_ASFLAGS="-f win32 -rnasm -pnasm -DPIC -DWIN32" + ;; + WINNT:x86_64) + LIBAV_FFT_ASFLAGS="-f win64 -rnasm -pnasm -D__x86_64__ -DPIC -DWIN64 -DMSVC" + ;; + *:x86_64) + if $CC -E -dM - 0) { + var top = stack.pop(); + + if (seen.has(top)) { + ok(false, + "This is a tree, not a graph: we shouldn't have multiple edges to the same node"); + } + seen.add(top); + if (seen.size % 1000 === 0) { + dumpn("Progress update: seen size = " + seen.size); + } + + var newNodes = dominatorTree.getImmediatelyDominated(top); + if (Object.prototype.toString.call(newNodes) !== "[object Array]") { + ok(false, "getImmediatelyDominated should return an array for known node ids"); + } + + var topSize = dominatorTree.getRetainedSize(top); + + var lastSize = Infinity; + for (var i = 0; i < newNodes.length; i++) { + if (typeof newNodes[i] !== "number") { + ok(false, "Every dominated id should be a number"); + } + + var thisSize = dominatorTree.getRetainedSize(newNodes[i]); + + if (thisSize >= topSize) { + ok(false, "the size of children in the dominator tree should always be less than that of their parent"); + } + + if (thisSize > lastSize) { + ok(false, + "children should be sorted by greatest to least retained size, " + + "lastSize = " + lastSize + ", thisSize = " + thisSize); + } + + lastSize = thisSize; + stack.push(newNodes[i]); + } + } + + ok(true, "Successfully walked the tree"); + dumpn("Walked " + seen.size + " nodes"); + + do_test_finished(); +} diff --git a/dom/camera/GonkRecorder.cpp b/dom/camera/GonkRecorder.cpp index 71e50725fa..4065839d93 100644 --- a/dom/camera/GonkRecorder.cpp +++ b/dom/camera/GonkRecorder.cpp @@ -41,7 +41,11 @@ #include #include #include +#if ANDROID_VERSION >= 21 +#include +#else #include +#endif #include #include @@ -195,10 +199,21 @@ GonkRecorder::GonkRecorder() GonkRecorder::~GonkRecorder() { RE_LOGV("Destructor"); stop(); + +#if ANDROID_VERSION >= 21 + if (mLooper != NULL) { + mLooper->stop(); + } +#endif } status_t GonkRecorder::init() { RE_LOGV("init"); +#if ANDROID_VERSION >= 21 + mLooper = new ALooper; + mLooper->setName("recorder_looper"); + mLooper->start(); +#endif return OK; } @@ -851,6 +866,71 @@ status_t GonkRecorder::start() { return status; } +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 +sp GonkRecorder::createAudioSource() { + sp audioSource = + new AudioSource( + mAudioSource, + mSampleRate, + mAudioChannels); + + status_t err = audioSource->initCheck(); + + if (err != OK) { + RE_LOGE("audio source is not initialized"); + return NULL; + } + + sp format = new AMessage; + switch (mAudioEncoder) { + case AUDIO_ENCODER_AMR_NB: + case AUDIO_ENCODER_DEFAULT: + format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB); + break; + case AUDIO_ENCODER_AMR_WB: + format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB); + break; + case AUDIO_ENCODER_AAC: + format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); + format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC); + break; + case AUDIO_ENCODER_HE_AAC: + format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); + format->setInt32("aac-profile", OMX_AUDIO_AACObjectHE); + break; + case AUDIO_ENCODER_AAC_ELD: + format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); + format->setInt32("aac-profile", OMX_AUDIO_AACObjectELD); + break; + + default: + RE_LOGE("Unknown audio encoder: %d", mAudioEncoder); + return NULL; + } + + int32_t maxInputSize; + CHECK(audioSource->getFormat()->findInt32( + kKeyMaxInputSize, &maxInputSize)); + + format->setInt32("max-input-size", maxInputSize); + format->setInt32("channel-count", mAudioChannels); + format->setInt32("sample-rate", mSampleRate); + format->setInt32("bitrate", mAudioBitRate); + if (mAudioTimeScale > 0) { + format->setInt32("time-scale", mAudioTimeScale); + } + + sp audioEncoder = + MediaCodecSource::Create(mLooper, format, audioSource); + mAudioSourceNode = audioSource; + + if (audioEncoder == NULL) { + RE_LOGE("Failed to create audio encoder"); + } + + return audioEncoder; +} +#else sp GonkRecorder::createAudioSource() { sp audioSource = new AudioSource( @@ -919,6 +999,7 @@ sp GonkRecorder::createAudioSource() { return audioEncoder; } +#endif #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 status_t GonkRecorder::startAACRecording() { @@ -1340,6 +1421,87 @@ status_t GonkRecorder::setupCameraSource( return OK; } +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 +status_t GonkRecorder::setupVideoEncoder( + sp cameraSource, + int32_t videoBitRate, + sp *source) { + source->clear(); + + sp format = new AMessage(); + + switch (mVideoEncoder) { + case VIDEO_ENCODER_H263: + format->setString("mime", MEDIA_MIMETYPE_VIDEO_H263); + break; + + case VIDEO_ENCODER_MPEG_4_SP: + format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4); + break; + + case VIDEO_ENCODER_H264: + format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC); + break; + + case VIDEO_ENCODER_VP8: + format->setString("mime", MEDIA_MIMETYPE_VIDEO_VP8); + break; + + default: + CHECK(!"Should not be here, unsupported video encoding."); + break; + } + + sp meta = cameraSource->getFormat(); + + int32_t width, height, stride, sliceHeight, colorFormat; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + CHECK(meta->findInt32(kKeyStride, &stride)); + CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); + CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); + + format->setInt32("width", width); + format->setInt32("height", height); + format->setInt32("stride", stride); + format->setInt32("slice-height", sliceHeight); + format->setInt32("color-format", colorFormat); + + format->setInt32("bitrate", videoBitRate); + format->setInt32("frame-rate", mFrameRate); + format->setInt32("i-frame-interval", mIFramesIntervalSec); + + if (mVideoTimeScale > 0) { + format->setInt32("time-scale", mVideoTimeScale); + } + if (mVideoEncoderProfile != -1) { + format->setInt32("profile", mVideoEncoderProfile); + } + if (mVideoEncoderLevel != -1) { + format->setInt32("level", mVideoEncoderLevel); + } + + uint32_t flags = 0; + if (mIsMetaDataStoredInVideoBuffers) { + flags |= MediaCodecSource::FLAG_USE_METADATA_INPUT; + } + + sp encoder = + MediaCodecSource::Create(mLooper, format, cameraSource, flags); + if (encoder == NULL) { + RE_LOGE("Failed to create video encoder"); + // When the encoder fails to be created, we need + // release the camera source due to the camera's lock + // and unlock mechanism. + cameraSource->stop(); + return UNKNOWN_ERROR; + } + + *source = encoder; + + return OK; +} +#else status_t GonkRecorder::setupVideoEncoder( sp cameraSource, int32_t videoBitRate, @@ -1426,6 +1588,7 @@ status_t GonkRecorder::setupVideoEncoder( return OK; } +#endif status_t GonkRecorder::setupAudioEncoder(const sp& writer) { status_t status = BAD_VALUE; diff --git a/dom/camera/GonkRecorder.h b/dom/camera/GonkRecorder.h index a43a43ab79..27b61967fb 100644 --- a/dom/camera/GonkRecorder.h +++ b/dom/camera/GonkRecorder.h @@ -24,6 +24,10 @@ #include #include #include +#if ANDROID_VERSION >= 21 +#include +#include +#endif #include "mozilla/RefPtr.h" #include "GonkCameraHwMgr.h" @@ -115,10 +119,10 @@ private: MediaProfiles *mEncoderProfiles; bool mStarted; - // Needed when GLFrames are encoded. - // An pointer - // will be sent to the client side using which the - // frame buffers will be queued and dequeued + +#if ANDROID_VERSION >= 21 + sp mLooper; +#endif sp mCameraHw; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 26294378a3..b7dc413a98 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -2204,10 +2204,6 @@ HTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult) NS_IMETHODIMP HTMLInputElement::SetUserInput(const nsAString& aValue) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } - if (mType == NS_FORM_INPUT_FILE) { Sequence list; diff --git a/dom/interfaces/core/nsIDOMNSEditableElement.idl b/dom/interfaces/core/nsIDOMNSEditableElement.idl index 22147223bf..67cb10488b 100644 --- a/dom/interfaces/core/nsIDOMNSEditableElement.idl +++ b/dom/interfaces/core/nsIDOMNSEditableElement.idl @@ -16,13 +16,13 @@ interface nsIEditor; * */ -[scriptable, uuid(b33eb56c-3120-418c-892b-774b00c7dde8)] +[scriptable, uuid(3503de34-6631-4594-b7be-c36ff6a520c4)] interface nsIDOMNSEditableElement : nsISupports { - readonly attribute nsIEditor editor; + [noscript] readonly attribute nsIEditor editor; // This is similar to set .value on nsIDOMInput/TextAreaElements, but // handling of the value change is closer to the normal user input, so // 'change' event for example will be dispatched when focusing out the // element. - void setUserInput(in DOMString input); + [noscript] void setUserInput(in DOMString input); }; diff --git a/dom/interfaces/security/nsIContentSecurityManager.idl b/dom/interfaces/security/nsIContentSecurityManager.idl index c8b263339f..8a54d16991 100644 --- a/dom/interfaces/security/nsIContentSecurityManager.idl +++ b/dom/interfaces/security/nsIContentSecurityManager.idl @@ -46,7 +46,7 @@ interface nsIContentSecurityManager : nsISupports * https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy * * This method should only be used when the context of the URI isn't available - * since isSecureContext is preffered as it handles parent contexts. + * since isSecureContext is preferred as it handles parent contexts. * * This method returns false instead of throwing upon errors. */ diff --git a/dom/media/AbstractMediaDecoder.h b/dom/media/AbstractMediaDecoder.h index a4534860c8..8ca77ce008 100644 --- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -27,6 +27,9 @@ class MediaResource; class ReentrantMonitor; class VideoFrameContainer; class MediaDecoderOwner; +#ifdef MOZ_EME +class CDMProxy; +#endif typedef nsDataHashtable MetadataTags; @@ -54,10 +57,6 @@ public: // by currentSrc. Returns what was passed to Load(), if Load() has been called. virtual MediaResource* GetResource() const = 0; - // Called by the decode thread to keep track of the number of bytes read - // from the resource. - virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) = 0; - // Increments the parsed, decoded and dropped frame counters by the passed in // counts. // Can be called on any thread. @@ -86,46 +85,16 @@ public: NS_DispatchToMainThread(r); } - // Set the media as being seekable or not. - virtual void SetMediaSeekable(bool aMediaSeekable) = 0; - - void DispatchSetMediaSeekable(bool aMediaSeekable) - { - nsCOMPtr r = NS_NewRunnableMethodWithArg( - this, &AbstractMediaDecoder::SetMediaSeekable, aMediaSeekable); - NS_DispatchToMainThread(r); - } - virtual VideoFrameContainer* GetVideoFrameContainer() = 0; virtual mozilla::layers::ImageContainer* GetImageContainer() = 0; - // Return true if the media layer supports seeking. - virtual bool IsTransportSeekable() = 0; - - // Return true if the transport layer supports seeking. - virtual bool IsMediaSeekable() = 0; - - virtual void MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags, MediaDecoderEventVisibility aEventVisibility) = 0; - virtual void FirstFrameLoaded(nsAutoPtr aInfo, MediaDecoderEventVisibility aEventVisibility) = 0; - - // May be called by the reader to notify this decoder that the metadata from - // the media file has been read. Call on the decode thread only. - virtual void OnReadMetadataCompleted() = 0; - // Returns the owner of this media decoder. The owner should only be used // on the main thread. virtual MediaDecoderOwner* GetOwner() = 0; - // Called by the reader's MediaResource as data arrives over the network. - // Must be called on the main thread. - virtual void NotifyDataArrived() = 0; - // Set by Reader if the current audio track can be offloaded virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {} - // Called from HTMLMediaElement when owner document activity changes - virtual void SetElementVisibility(bool aIsVisible) {} - // Stack based class to assist in notifying the frame statistics of // parsed and decoded frames. Use inside video demux & decode functions // to ensure all parsed and decoded frames are reported on all return paths. @@ -152,58 +121,6 @@ public: { MOZ_CRASH("Forbidden method"); return NS_OK; } }; -class MetadataContainer -{ -protected: - MetadataContainer(AbstractMediaDecoder* aDecoder, - nsAutoPtr aInfo, - nsAutoPtr aTags, - MediaDecoderEventVisibility aEventVisibility) - : mDecoder(aDecoder), - mInfo(aInfo), - mTags(aTags), - mEventVisibility(aEventVisibility) - {} - - RefPtr mDecoder; - nsAutoPtr mInfo; - nsAutoPtr mTags; - MediaDecoderEventVisibility mEventVisibility; -}; - -class MetadataEventRunner : public nsRunnable, private MetadataContainer -{ -public: - MetadataEventRunner(AbstractMediaDecoder* aDecoder, - nsAutoPtr aInfo, - nsAutoPtr aTags, - MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable) - : MetadataContainer(aDecoder, aInfo, aTags, aEventVisibility) - {} - - NS_IMETHOD Run() override - { - mDecoder->MetadataLoaded(mInfo, mTags, mEventVisibility); - return NS_OK; - } -}; - -class FirstFrameLoadedEventRunner : public nsRunnable, private MetadataContainer -{ -public: - FirstFrameLoadedEventRunner(AbstractMediaDecoder* aDecoder, - nsAutoPtr aInfo, - MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable) - : MetadataContainer(aDecoder, aInfo, nsAutoPtr(nullptr), aEventVisibility) - {} - - NS_IMETHOD Run() override - { - mDecoder->FirstFrameLoaded(mInfo, mEventVisibility); - return NS_OK; - } -}; - } // namespace mozilla #endif diff --git a/dom/media/EncodedBufferCache.h b/dom/media/EncodedBufferCache.h index e0a921a823..3baaa05aa5 100644 --- a/dom/media/EncodedBufferCache.h +++ b/dom/media/EncodedBufferCache.h @@ -17,7 +17,7 @@ namespace mozilla { namespace dom { class Blob; -} +} // namespace dom /** * Data is moved into a temporary file when it grows beyond diff --git a/dom/media/FrameStatistics.h b/dom/media/FrameStatistics.h index 73d48730d4..9928719ab3 100644 --- a/dom/media/FrameStatistics.h +++ b/dom/media/FrameStatistics.h @@ -13,6 +13,8 @@ namespace mozilla { // Threadsafe. class FrameStatistics { public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FrameStatistics); + FrameStatistics() : mReentrantMonitor("FrameStats"), mParsedFrames(0), @@ -80,6 +82,7 @@ public: } private: + ~FrameStatistics() {} // ReentrantMonitor to protect access of playback statistics. ReentrantMonitor mReentrantMonitor; diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 634f8d731e..ce86fe3089 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -496,9 +496,13 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner) , mLogicalPosition(0.0) , mDuration(std::numeric_limits::quiet_NaN()) , mResourceCallback(new ResourceCallback()) +#ifdef MOZ_EME + , mCDMProxyPromise(mCDMProxyPromiseHolder.Ensure(__func__)) +#endif , mIgnoreProgressData(false) , mInfiniteStream(false) , mOwner(aOwner) + , mFrameStats(new FrameStatistics()) , mVideoFrameContainer(aOwner->GetVideoFrameContainer()) , mPlaybackStatistics(new MediaChannelStatistics()) , mPinnedForSeek(false) @@ -600,12 +604,24 @@ MediaDecoder::Shutdown() mResourceCallback->Disconnect(); +#ifdef MOZ_EME + mCDMProxyPromiseHolder.RejectIfExists(true, __func__); +#endif + // This changes the decoder state to SHUTDOWN and does other things // necessary to unblock the state machine thread if it's blocked, so // the asynchronous shutdown in nsDestroyStateMachine won't deadlock. if (mDecoderStateMachine) { - mDecoderStateMachine->DispatchShutdown(); mTimedMetadataListener.Disconnect(); + mMetadataLoadedListener.Disconnect(); + mFirstFrameLoadedListener.Disconnect(); + mOnPlaybackEvent.Disconnect(); + mOnSeekingStart.Disconnect(); + mOnMediaNotSeekable.Disconnect(); + + mDecoderStateMachine->BeginShutdown()->Then( + AbstractThread::MainThread(), __func__, this, + &MediaDecoder::FinishShutdown, &MediaDecoder::FinishShutdown); } // Force any outstanding seek and byterange requests to complete @@ -629,6 +645,37 @@ MediaDecoder::~MediaDecoder() MOZ_COUNT_DTOR(MediaDecoder); } +void +MediaDecoder::OnPlaybackEvent(MediaEventType aEvent) +{ + switch (aEvent) { + case MediaEventType::PlaybackStarted: + mPlaybackStatistics->Start(); + break; + case MediaEventType::PlaybackStopped: + mPlaybackStatistics->Stop(); + ComputePlaybackRate(); + break; + case MediaEventType::PlaybackEnded: + PlaybackEnded(); + break; + case MediaEventType::DecodeError: + DecodeError(); + break; + case MediaEventType::Invalidate: + Invalidate(); + break; + } +} + +void +MediaDecoder::FinishShutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + mDecoderStateMachine->BreakCycles(); + SetStateMachine(nullptr); +} + MediaResourceCallback* MediaDecoder::GetResourceCallback() const { @@ -685,6 +732,17 @@ MediaDecoder::SetStateMachineParameters() } mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect( AbstractThread::MainThread(), this, &MediaDecoder::OnMetadataUpdate); + mMetadataLoadedListener = mDecoderStateMachine->MetadataLoadedEvent().Connect( + AbstractThread::MainThread(), this, &MediaDecoder::MetadataLoaded); + mFirstFrameLoadedListener = mDecoderStateMachine->FirstFrameLoadedEvent().Connect( + AbstractThread::MainThread(), this, &MediaDecoder::FirstFrameLoaded); + + mOnPlaybackEvent = mDecoderStateMachine->OnPlaybackEvent().Connect( + AbstractThread::MainThread(), this, &MediaDecoder::OnPlaybackEvent); + mOnSeekingStart = mDecoderStateMachine->OnSeekingStart().Connect( + AbstractThread::MainThread(), this, &MediaDecoder::SeekingStarted); + mOnMediaNotSeekable = mDecoderStateMachine->OnMediaNotSeekable().Connect( + AbstractThread::MainThread(), this, &MediaDecoder::OnMediaNotSeekable); } void @@ -792,15 +850,13 @@ MediaDecoder::MetadataLoaded(nsAutoPtr aInfo, MediaDecoderEventVisibility aEventVisibility) { MOZ_ASSERT(NS_IsMainThread()); - - if (mShuttingDown) { - return; - } + MOZ_ASSERT(!mShuttingDown); DECODER_LOG("MetadataLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d", aInfo->mAudio.mChannels, aInfo->mAudio.mRate, aInfo->HasAudio(), aInfo->HasVideo()); + SetMediaSeekable(aInfo->mMediaSeekable); mInfo = aInfo.forget(); ConstructMediaTracks(); @@ -869,10 +925,7 @@ MediaDecoder::FirstFrameLoaded(nsAutoPtr aInfo, MediaDecoderEventVisibility aEventVisibility) { MOZ_ASSERT(NS_IsMainThread()); - - if (mShuttingDown) { - return; - } + MOZ_ASSERT(!mShuttingDown); DECODER_LOG("FirstFrameLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d mPlayState=%s mIsDormant=%d", aInfo->mAudio.mChannels, aInfo->mAudio.mRate, @@ -1499,13 +1552,6 @@ MediaDecoder::GetStateMachine() const { return mDecoderStateMachine; } -// Drop reference to state machine. Only called during shutdown dance. -void -MediaDecoder::BreakCycles() { - MOZ_ASSERT(NS_IsMainThread()); - SetStateMachine(nullptr); -} - void MediaDecoder::FireTimeUpdate() { @@ -1548,6 +1594,34 @@ MediaDecoder::CanPlayThrough() return mDecoderStateMachine->IsRealTime() || GetStatistics().CanPlayThrough(); } +#ifdef MOZ_EME +RefPtr +MediaDecoder::RequestCDMProxy() const +{ + return mCDMProxyPromise; +} + +void +MediaDecoder::SetCDMProxy(CDMProxy* aProxy) +{ + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr proxy = aProxy; + { + CDMCaps::AutoLock caps(aProxy->Capabilites()); + if (!caps.AreCapsKnown()) { + RefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([=] () { + self->mCDMProxyPromiseHolder.ResolveIfExists(proxy, __func__); + }); + caps.CallOnMainThreadWhenCapsAvailable(r); + return; + } + } + mCDMProxyPromiseHolder.ResolveIfExists(proxy, __func__); +} +#endif + #ifdef MOZ_RAW bool MediaDecoder::IsRawEnabled() diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index d15e43c165..b27e8a671a 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -184,6 +184,10 @@ destroying the MediaDecoder object. #if !defined(MediaDecoder_h_) #define MediaDecoder_h_ +#ifdef MOZ_EME +#include "mozilla/CDMProxy.h" +#endif + #include "mozilla/Atomics.h" #include "mozilla/MozPromise.h" #include "mozilla/ReentrantMonitor.h" @@ -218,6 +222,8 @@ namespace mozilla { class VideoFrameContainer; class MediaDecoderStateMachine; +enum class MediaEventType : int8_t; + // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to // GetTickCount() and conflicts with MediaDecoder::GetCurrentTime implementation. #ifdef GetCurrentTime @@ -438,7 +444,7 @@ public: // Called as data arrives on the stream and is read into the cache. Called // on the main thread only. - virtual void NotifyDataArrived() override; + void NotifyDataArrived(); // Return true if we are currently seeking in the media resource. // Call on the main thread only. @@ -459,17 +465,20 @@ protected: // changed, this causes a durationchanged event to fire to the media // element. void UpdateEstimatedMediaDuration(int64_t aDuration) override; + public: + // Called from HTMLMediaElement when owner document activity changes + virtual void SetElementVisibility(bool aIsVisible) {} // Set a flag indicating whether seeking is supported - virtual void SetMediaSeekable(bool aMediaSeekable) override; + void SetMediaSeekable(bool aMediaSeekable); // Returns true if this media supports seeking. False for example for WebM // files without an index and chained ogg files. - virtual bool IsMediaSeekable() final override; + bool IsMediaSeekable(); // Returns true if seeking is supported on a transport level (e.g. the server // supports range requests, we are playing a file, etc.). - virtual bool IsTransportSeekable() override; + bool IsTransportSeekable(); // Return the time ranges that can be seeked into. virtual media::TimeIntervals GetSeekable(); @@ -563,26 +572,6 @@ private: // so recompute it. The monitor must be held. virtual void UpdatePlaybackRate(); - // Used to estimate rates of data passing through the decoder's channel. - // Records activity stopping on the channel. - void DispatchPlaybackStarted() { - RefPtr self = this; - nsCOMPtr r = - NS_NewRunnableFunction([self] () { self->mPlaybackStatistics->Start(); }); - AbstractThread::MainThread()->Dispatch(r.forget()); - } - - // Used to estimate rates of data passing through the decoder's channel. - // Records activity stopping on the channel. - void DispatchPlaybackStopped() { - RefPtr self = this; - nsCOMPtr r = NS_NewRunnableFunction([self] () { - self->mPlaybackStatistics->Stop(); - self->ComputePlaybackRate(); - }); - AbstractThread::MainThread()->Dispatch(r.forget()); - } - // The actual playback rate computation. The monitor must be held. void ComputePlaybackRate(); @@ -603,21 +592,6 @@ private: // change. Call on the main thread only. virtual void ChangeState(PlayState aState); - // May be called by the reader to notify this decoder that the metadata from - // the media file has been read. Call on the decode thread only. - void OnReadMetadataCompleted() override { } - - // Called when the metadata from the media file has been loaded by the - // state machine. Call on the main thread only. - virtual void MetadataLoaded(nsAutoPtr aInfo, - nsAutoPtr aTags, - MediaDecoderEventVisibility aEventVisibility) override; - - // Called when the first audio and/or video from the media file has been loaded - // by the state machine. Call on the main thread only. - virtual void FirstFrameLoaded(nsAutoPtr aInfo, - MediaDecoderEventVisibility aEventVisibility) override; - // Called from MetadataLoaded(). Creates audio tracks and adds them to its // owner's audio track list, and implies to video tracks respectively. // Call on the main thread only. @@ -662,9 +636,6 @@ private: // position. int64_t GetDownloadPosition(); - // Drop reference to state machine. Only called during shutdown dance. - virtual void BreakCycles(); - // Notifies the element that decoding has failed. void DecodeError(); @@ -673,6 +644,16 @@ private: MediaDecoderOwner* GetOwner() override; +#ifdef MOZ_EME + typedef MozPromise, bool /* aIgnored */, /* IsExclusive = */ true> CDMProxyPromise; + + // Resolved when a CDMProxy is available and the capabilities are known or + // rejected when this decoder is about to shut down. + RefPtr RequestCDMProxy() const; + + void SetCDMProxy(CDMProxy* aProxy); +#endif + void EnsureTelemetryReported(); #ifdef MOZ_RAW @@ -716,7 +697,7 @@ private: MediaStatistics GetStatistics(); // Return the frame decode/paint related statistics. - FrameStatistics& GetFrameStatistics() { return mFrameStats; } + FrameStatistics& GetFrameStatistics() { return *mFrameStats; } // Increments the parsed and decoded frame counters by the passed in counts. // Can be called on any thread. @@ -739,6 +720,12 @@ private: protected: virtual ~MediaDecoder(); + + // Called when the first audio and/or video from the media file has been loaded + // by the state machine. Call on the main thread only. + virtual void FirstFrameLoaded(nsAutoPtr aInfo, + MediaDecoderEventVisibility aEventVisibility); + void SetStateMachineParameters(); static void DormantTimerExpired(nsITimer *aTimer, void *aClosure); @@ -803,15 +790,30 @@ protected: // Media data resource. RefPtr mResource; - // Amount of buffered data ahead of current time required to consider that + // Amount of buffered data ahead of current time required to consider that // the next frame is available. // An arbitrary value of 250ms is used. static const int DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED = 250000; private: + // Called when the metadata from the media file has been loaded by the + // state machine. Call on the main thread only. + void MetadataLoaded(nsAutoPtr aInfo, + nsAutoPtr aTags, + MediaDecoderEventVisibility aEventVisibility); + MediaEventSource* DataArrivedEvent() override { return &mDataArrivedEvent; } + void OnPlaybackEvent(MediaEventType aEvent); + + void OnMediaNotSeekable() + { + SetMediaSeekable(false); + } + + void FinishShutdown(); + MediaEventProducer mDataArrivedEvent; // The state machine object for handling the decoding. It is safe to @@ -825,8 +827,12 @@ private: RefPtr mResourceCallback; -protected: +#ifdef MOZ_EME + MozPromiseHolder mCDMProxyPromiseHolder; + RefPtr mCDMProxyPromise; +#endif +protected: virtual void CallSeek(const SeekTarget& aTarget); // Returns true if heuristic dormant is supported. @@ -859,7 +865,7 @@ protected: MediaDecoderOwner* const mOwner; // Counters related to decode and presentation of frames. - FrameStatistics mFrameStats; + const RefPtr mFrameStats; const RefPtr mVideoFrameContainer; @@ -926,6 +932,13 @@ protected: // A listener to receive metadata updates from MDSM. MediaEventListener mTimedMetadataListener; + MediaEventListener mMetadataLoadedListener; + MediaEventListener mFirstFrameLoadedListener; + + MediaEventListener mOnPlaybackEvent; + MediaEventListener mOnSeekingStart; + MediaEventListener mOnMediaNotSeekable; + protected: // Whether the state machine is shut down. Mirror mStateMachineIsShutdown; @@ -1074,13 +1087,13 @@ private: // Called by the MediaResource to keep track of the number of bytes read // from the resource. Called on the main by an event runner dispatched // by the MediaResource read functions. - void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override; + void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset); // Called by nsChannelToPipeListener or MediaResource when the // download has ended. Called on the main thread only. aStatus is // the result from OnStopRequest. void NotifyDownloadEnded(nsresult aStatus); - + bool mTelemetryReported; }; diff --git a/dom/media/MediaDecoderOwner.h b/dom/media/MediaDecoderOwner.h index 7c08372f86..dda03b7578 100644 --- a/dom/media/MediaDecoderOwner.h +++ b/dom/media/MediaDecoderOwner.h @@ -129,6 +129,14 @@ public: // Called by the media decoder object, on the main thread, // when the connection between Rtsp server and client gets lost. virtual void ResetConnectionState() = 0; + +#ifdef MOZ_EME + // Dispatches a "encrypted" event to the HTMLMediaElement, with the + // provided init data. Actual dispatch may be delayed until HAVE_METADATA. + // Main thread only. + virtual void DispatchEncrypted(const nsTArray& aInitData, + const nsAString& aInitDataType) = 0; +#endif // MOZ_EME }; } // namespace mozilla diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index ebef9aa02f..1cc1d55f0c 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -20,6 +20,7 @@ namespace mozilla { +class CDMProxy; class MediaDecoderReader; struct WaitForDataRejectValue @@ -190,6 +191,10 @@ public: // when to call SetIdle(). virtual void SetIdle() {} +#ifdef MOZ_EME + virtual void SetCDMProxy(CDMProxy* aProxy) {} +#endif + // Tell the reader that the data decoded are not for direct playback, so it // can accept more files, in particular those which have more channels than // available in the audio output. @@ -234,10 +239,6 @@ public: return &mBuffered; } - // Indicates if the media is seekable. - // ReadMetada should be called before calling this method. - virtual bool IsMediaSeekable() = 0; - void DispatchSetStartTime(int64_t aStartTime) { RefPtr self = this; @@ -274,6 +275,9 @@ public: return mTimedMetadataEvent; } + // Notified by the OggReader during playback when chained ogg is detected. + MediaEventSource& OnMediaNotSeekable() { return mOnMediaNotSeekable; } + protected: virtual ~MediaDecoderReader(); @@ -364,6 +368,9 @@ protected: // Used to send TimedMetadata to the listener. TimedMetadataEventProducer mTimedMetadataEvent; + // Notify if this media is not seekable. + MediaEventProducer mOnMediaNotSeekable; + private: // Does any spinup that needs to happen on this task queue. This runs on a // different thread than Init, and there should not be ordering dependencies diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 89e9ef7e8f..6d3c515be7 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -62,7 +62,7 @@ using namespace mozilla::media; #undef VERBOSE_LOG #define LOG(m, l, x, ...) \ - MOZ_LOG(m, l, ("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__)) + MOZ_LOG(m, l, ("Decoder=%p " x, mDecoderID, ##__VA_ARGS__)) #define DECODER_LOG(x, ...) \ LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__) #define VERBOSE_LOG(x, ...) \ @@ -74,7 +74,7 @@ using namespace mozilla::media; // when __VA_ARGS__ expands to nothing. This is a workaround for it. #define DECODER_WARN_HELPER(a, b) NS_WARNING b #define DECODER_WARN(x, ...) \ - DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__).get())) + DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get())) // Certain constants get stored as member variables and then adjusted by various // scale factors on a per-decoder basis. We want to make sure to avoid using these @@ -199,7 +199,10 @@ static void InitVideoQueuePrefs() { MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, MediaDecoderReader* aReader, bool aRealTime) : - mDecoder(aDecoder), + mDecoderID(aDecoder), + mFrameStats(&aDecoder->GetFrameStatistics()), + mVideoFrameContainer(aDecoder->GetVideoFrameContainer()), + mAudioChannel(aDecoder->GetAudioChannel()), mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), /* aSupportsTailDispatch = */ true)), mWatchManager(this, mTaskQueue), @@ -280,7 +283,8 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); // Dispatch initialization that needs to happen on that task queue. - nsCOMPtr r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::InitializationTask); + nsCOMPtr r = NS_NewRunnableMethodWithArg>( + this, &MediaDecoderStateMachine::InitializationTask, aDecoder); mTaskQueue->Dispatch(r.forget()); InitVideoQueuePrefs(); @@ -305,6 +309,13 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread()); mMediaSink = CreateMediaSink(mAudioCaptured); + +#ifdef MOZ_EME + mCDMProxyPromise.Begin(aDecoder->RequestCDMProxy()->Then( + OwnerThread(), __func__, this, + &MediaDecoderStateMachine::OnCDMProxyReady, + &MediaDecoderStateMachine::OnCDMProxyNotReady)); +#endif } MediaDecoderStateMachine::~MediaDecoderStateMachine() @@ -320,25 +331,25 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine() } void -MediaDecoderStateMachine::InitializationTask() +MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder) { MOZ_ASSERT(OnTaskQueue()); // Connect mirrors. mBuffered.Connect(mReader->CanonicalBuffered()); - mEstimatedDuration.Connect(mDecoder->CanonicalEstimatedDuration()); - mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration()); - mPlayState.Connect(mDecoder->CanonicalPlayState()); - mNextPlayState.Connect(mDecoder->CanonicalNextPlayState()); - mLogicallySeeking.Connect(mDecoder->CanonicalLogicallySeeking()); - mVolume.Connect(mDecoder->CanonicalVolume()); - mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate()); - mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch()); - mSameOriginMedia.Connect(mDecoder->CanonicalSameOriginMedia()); - mPlaybackBytesPerSecond.Connect(mDecoder->CanonicalPlaybackBytesPerSecond()); - mPlaybackRateReliable.Connect(mDecoder->CanonicalPlaybackRateReliable()); - mDecoderPosition.Connect(mDecoder->CanonicalDecoderPosition()); - mMediaSeekable.Connect(mDecoder->CanonicalMediaSeekable()); + mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration()); + mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration()); + mPlayState.Connect(aDecoder->CanonicalPlayState()); + mNextPlayState.Connect(aDecoder->CanonicalNextPlayState()); + mLogicallySeeking.Connect(aDecoder->CanonicalLogicallySeeking()); + mVolume.Connect(aDecoder->CanonicalVolume()); + mLogicalPlaybackRate.Connect(aDecoder->CanonicalPlaybackRate()); + mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch()); + mSameOriginMedia.Connect(aDecoder->CanonicalSameOriginMedia()); + mPlaybackBytesPerSecond.Connect(aDecoder->CanonicalPlaybackBytesPerSecond()); + mPlaybackRateReliable.Connect(aDecoder->CanonicalPlaybackRateReliable()); + mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition()); + mMediaSeekable.Connect(aDecoder->CanonicalMediaSeekable()); // Initialize watchers. mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated); @@ -368,7 +379,7 @@ MediaDecoderStateMachine::CreateAudioSink() MOZ_ASSERT(self->OnTaskQueue()); return new DecodedAudioDataSink( self->mAudioQueue, self->GetMediaTime(), - self->mInfo.mAudio, self->mDecoder->GetAudioChannel()); + self->mInfo.mAudio, self->mAudioChannel); }; return new AudioSinkWrapper(mTaskQueue, audioSinkCreator); } @@ -384,8 +395,8 @@ MediaDecoderStateMachine::CreateMediaSink(bool aAudioCaptured) RefPtr mediaSink = new VideoSink(mTaskQueue, audioSink, mVideoQueue, - mDecoder->GetVideoFrameContainer(), mRealTime, - mDecoder->GetFrameStatistics(), AUDIO_DURATION_USECS, + mVideoFrameContainer, mRealTime, + *mFrameStats, sVideoQueueSendToCompositorSize); return mediaSink.forget(); } @@ -887,19 +898,6 @@ MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample) StopPrerollingVideo(); } - // Schedule the state machine to send stream data as soon as possible if - // the VideoQueue() is empty or contains one frame before the Push(). - // - // The state machine threads requires a frame in VideoQueue() that is `in - // the future` to gather precise timing information. The head of - // VideoQueue() is always `in the past`. - // - // Schedule the state machine as soon as possible to render the video - // frame or delay the state machine thread accurately. - if (VideoQueue().GetSize() <= 2) { - ScheduleStateMachine(); - } - // For non async readers, if the requested video sample was slow to // arrive, increase the amount of audio we buffer to ensure that we // don't run out of audio. This is unnecessary for async readers, @@ -1058,7 +1056,7 @@ void MediaDecoderStateMachine::StopPlayback() MOZ_ASSERT(OnTaskQueue()); DECODER_LOG("StopPlayback()"); - mDecoder->DispatchPlaybackStopped(); + mOnPlaybackEvent.Notify(MediaEventType::PlaybackStopped); if (IsPlaying()) { mMediaSink->SetPlaying(false); @@ -1091,7 +1089,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback() } DECODER_LOG("MaybeStartPlayback() starting playback"); - mDecoder->DispatchPlaybackStarted(); + mOnPlaybackEvent.Notify(MediaEventType::PlaybackStarted); StartMediaSink(); if (!IsPlaying()) { @@ -1303,7 +1301,8 @@ MediaDecoderStateMachine::SetDormant(bool aDormant) } } -void MediaDecoderStateMachine::Shutdown() +RefPtr +MediaDecoderStateMachine::Shutdown() { MOZ_ASSERT(OnTaskQueue()); @@ -1317,6 +1316,10 @@ void MediaDecoderStateMachine::Shutdown() mPendingSeek.RejectIfExists(__func__); mCurrentSeek.RejectIfExists(__func__); +#ifdef MOZ_EME + mCDMProxyPromise.DisconnectIfExists(); +#endif + if (IsPlaying()) { StopPlayback(); } @@ -1330,13 +1333,16 @@ void MediaDecoderStateMachine::Shutdown() mStartTimeRendezvous->Destroy(); } + DECODER_LOG("Shutdown started"); + // Put a task in the decode queue to shutdown the reader. // the queue to spin down. - InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::Shutdown) + return InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__, + &MediaDecoderReader::Shutdown) ->Then(OwnerThread(), __func__, this, &MediaDecoderStateMachine::FinishShutdown, - &MediaDecoderStateMachine::FinishShutdown); - DECODER_LOG("Shutdown started"); + &MediaDecoderStateMachine::FinishShutdown) + ->CompletionPromise(); } void MediaDecoderStateMachine::StartDecoding() @@ -1599,12 +1605,7 @@ MediaDecoderStateMachine::InitiateSeek() StopPlayback(); UpdatePlaybackPositionInternal(mCurrentSeek.mTarget.mTime); - nsCOMPtr startEvent = - NS_NewRunnableMethodWithArg( - mDecoder, - &MediaDecoder::SeekingStarted, - mCurrentSeek.mTarget.mEventVisibility); - AbstractThread::MainThread()->Dispatch(startEvent.forget()); + mOnSeekingStart.Notify(mCurrentSeek.mTarget.mEventVisibility); // Reset our state machine and decoding pipeline before seeking. Reset(); @@ -1897,9 +1898,7 @@ MediaDecoderStateMachine::DecodeError() // MediaDecoder::DecodeError notifies the owner, and then shuts down the state // machine. - nsCOMPtr event = - NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError); - AbstractThread::MainThread()->Dispatch(event.forget()); + mOnPlaybackEvent.Notify(MediaEventType::DecodeError); } void @@ -1916,7 +1915,6 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata) // Set mode to PLAYBACK after reading metadata. mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK); - mDecoder->DispatchSetMediaSeekable(mReader->IsMediaSeekable()); mInfo = aMetadata->mInfo; mMetadataTags = aMetadata->mTags.forget(); RefPtr self = this; @@ -1964,7 +1962,11 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata) // thus get the metadata). We could fix this if we could compute the start // time by demuxing without necessaring decoding. bool waitingForCDM = +#ifdef MOZ_EME + mInfo.IsEncrypted() && !mCDMProxy; +#else false; +#endif mNotifyMetadataBeforeFirstFrame = mDuration.Ref().isSome() || waitingForCDM; if (mNotifyMetadataBeforeFirstFrame) { EnqueueLoadedMetadataEvent(); @@ -1996,14 +1998,12 @@ void MediaDecoderStateMachine::EnqueueLoadedMetadataEvent() { MOZ_ASSERT(OnTaskQueue()); - nsAutoPtr info(new MediaInfo()); - *info = mInfo; - MediaDecoderEventVisibility visibility = mSentLoadedMetadataEvent? - MediaDecoderEventVisibility::Suppressed : - MediaDecoderEventVisibility::Observable; - nsCOMPtr metadataLoadedEvent = - new MetadataEventRunner(mDecoder, info, mMetadataTags, visibility); - AbstractThread::MainThread()->Dispatch(metadataLoadedEvent.forget()); + MediaDecoderEventVisibility visibility = + mSentLoadedMetadataEvent ? MediaDecoderEventVisibility::Suppressed + : MediaDecoderEventVisibility::Observable; + mMetadataLoadedEvent.Notify(nsAutoPtr(new MediaInfo(mInfo)), + Move(mMetadataTags), + Move(visibility)); mSentLoadedMetadataEvent = true; } @@ -2011,14 +2011,11 @@ void MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent() { MOZ_ASSERT(OnTaskQueue()); - nsAutoPtr info(new MediaInfo()); - *info = mInfo; - MediaDecoderEventVisibility visibility = mSentFirstFrameLoadedEvent? - MediaDecoderEventVisibility::Suppressed : - MediaDecoderEventVisibility::Observable; - nsCOMPtr event = - new FirstFrameLoadedEventRunner(mDecoder, info, visibility); - AbstractThread::MainThread()->Dispatch(event.forget()); + MediaDecoderEventVisibility visibility = + mSentFirstFrameLoadedEvent ? MediaDecoderEventVisibility::Suppressed + : MediaDecoderEventVisibility::Observable; + mFirstFrameLoadedEvent.Notify(nsAutoPtr(new MediaInfo(mInfo)), + Move(visibility)); mSentFirstFrameLoadedEvent = true; } @@ -2159,46 +2156,19 @@ MediaDecoderStateMachine::SeekCompleted() if (video) { mMediaSink->Redraw(); - nsCOMPtr event = - NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate); - AbstractThread::MainThread()->Dispatch(event.forget()); + mOnPlaybackEvent.Notify(MediaEventType::Invalidate); } } -class DecoderDisposer -{ -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecoderDisposer) - DecoderDisposer(MediaDecoder* aDecoder, MediaDecoderStateMachine* aStateMachine) - : mDecoder(aDecoder), mStateMachine(aStateMachine) {} - - void OnTaskQueueShutdown() - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mStateMachine); - MOZ_ASSERT(mDecoder); - mStateMachine->BreakCycles(); - mDecoder->BreakCycles(); - mStateMachine = nullptr; - mDecoder = nullptr; - } - -private: - virtual ~DecoderDisposer() {} - RefPtr mDecoder; - RefPtr mStateMachine; -}; - -void -MediaDecoderStateMachine::DispatchShutdown() +RefPtr +MediaDecoderStateMachine::BeginShutdown() { mStreamSink->BeginShutdown(); - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &MediaDecoderStateMachine::Shutdown); - OwnerThread()->Dispatch(runnable.forget()); + return InvokeAsync(OwnerThread(), this, __func__, + &MediaDecoderStateMachine::Shutdown); } -void +RefPtr MediaDecoderStateMachine::FinishShutdown() { MOZ_ASSERT(OnTaskQueue()); @@ -2239,25 +2209,8 @@ MediaDecoderStateMachine::FinishShutdown() MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN, "How did we escape from the shutdown state?"); - // We must daisy-chain these events to destroy the decoder. We must - // destroy the decoder on the main thread, but we can't destroy the - // decoder while this thread holds the decoder monitor. We can't - // dispatch an event to the main thread to destroy the decoder from - // here, as the event may run before the dispatch returns, and we - // hold the decoder monitor here. We also want to guarantee that the - // state machine is destroyed on the main thread, and so the - // event runner running this function (which holds a reference to the - // state machine) needs to finish and be released in order to allow - // that. So we dispatch an event to run after this event runner has - // finished and released its monitor/references. That event then will - // dispatch an event to the main thread to release the decoder and - // state machine. DECODER_LOG("Shutting down state machine task queue"); - RefPtr disposer = new DecoderDisposer(mDecoder, this); - OwnerThread()->BeginShutdown()->Then(AbstractThread::MainThread(), __func__, - disposer.get(), - &DecoderDisposer::OnTaskQueueShutdown, - &DecoderDisposer::OnTaskQueueShutdown); + return OwnerThread()->BeginShutdown(); } nsresult MediaDecoderStateMachine::RunStateMachine() @@ -2414,9 +2367,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine() // Ensure readyState is updated before firing the 'ended' event. UpdateNextFrameStatus(); - nsCOMPtr event = - NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded); - AbstractThread::MainThread()->Dispatch(event.forget()); + mOnPlaybackEvent.Notify(MediaEventType::PlaybackEnded); mSentPlaybackEndedEvent = true; @@ -2482,7 +2433,7 @@ MediaDecoderStateMachine::CheckFrameValidity(VideoData* aData) // Update corrupt-frames statistics if (aData->mImage && !aData->mImage->IsValid()) { - FrameStatistics& frameStats = mDecoder->GetFrameStatistics(); + FrameStatistics& frameStats = *mFrameStats; frameStats.NotifyCorruptFrame(); // If more than 10% of the last 30 frames have been corrupted, then try disabling // hardware acceleration. We use 10 as the corrupt value because RollingMean<> @@ -2915,6 +2866,27 @@ void MediaDecoderStateMachine::OnMediaSinkAudioError() DecodeError(); } +#ifdef MOZ_EME +void +MediaDecoderStateMachine::OnCDMProxyReady(RefPtr aProxy) +{ + MOZ_ASSERT(OnTaskQueue()); + mCDMProxyPromise.Complete(); + mCDMProxy = aProxy; + mReader->SetCDMProxy(aProxy); + if (mState == DECODER_STATE_WAIT_FOR_CDM) { + StartDecoding(); + } +} + +void +MediaDecoderStateMachine::OnCDMProxyNotReady() +{ + MOZ_ASSERT(OnTaskQueue()); + mCDMProxyPromise.Complete(); +} +#endif + void MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured) { diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 0b23f9a915..2816602a06 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -110,6 +110,14 @@ class TaskQueue; extern LazyLogModule gMediaDecoderLog; extern LazyLogModule gMediaSampleLog; +enum class MediaEventType : int8_t { + PlaybackStarted, + PlaybackStopped, + PlaybackEnded, + DecodeError, + Invalidate +}; + /* The state machine class. This manages the decoding and seeking in the MediaDecoderReader on the decode task queue, and A/V sync on the shared @@ -159,7 +167,7 @@ public: // Set/Unset dormant state. void DispatchSetDormant(bool aDormant); - void DispatchShutdown(); + RefPtr BeginShutdown(); void DispatchStartBuffering() { @@ -210,20 +218,38 @@ public: OwnerThread()->Dispatch(r.forget()); } - // Drop reference to decoder. Only called during shutdown dance. + // Drop reference to mReader and mResource. Only called during shutdown dance. void BreakCycles() { MOZ_ASSERT(NS_IsMainThread()); if (mReader) { mReader->BreakCycles(); } mResource = nullptr; - mDecoder = nullptr; } TimedMetadataEventSource& TimedMetadataEvent() { return mMetadataManager.TimedMetadataEvent(); } + MediaEventSource& OnMediaNotSeekable() { + return mReader->OnMediaNotSeekable(); + } + + MediaEventSourceExc, + nsAutoPtr, + MediaDecoderEventVisibility>& + MetadataLoadedEvent() { return mMetadataLoadedEvent; } + + MediaEventSourceExc, + MediaDecoderEventVisibility>& + FirstFrameLoadedEvent() { return mFirstFrameLoadedEvent; } + + MediaEventSource& + OnPlaybackEvent() { return mOnPlaybackEvent; } + + MediaEventSource& + OnSeekingStart() { return mOnSeekingStart; } + // Immutable after construction - may be called on any thread. bool IsRealTime() const { return mRealTime; } @@ -249,7 +275,7 @@ private: // Initialization that needs to happen on the task queue. This is the first // task that gets run on the task queue, and is dispatched from the MDSM // constructor immediately after the task queue is created. - void InitializationTask(); + void InitializationTask(MediaDecoder* aDecoder); void SetDormant(bool aDormant); @@ -257,9 +283,9 @@ private: RefPtr Seek(SeekTarget aTarget); - void Shutdown(); + RefPtr Shutdown(); - void FinishShutdown(); + RefPtr FinishShutdown(); // Update the playback position. This can result in a timeupdate event // and an invalidate of the frame being dispatched asynchronously if @@ -639,16 +665,10 @@ private: void AdjustAudioThresholds(); - // The decoder object that created this state machine. The state machine - // holds a strong reference to the decoder to ensure that the decoder stays - // alive once media element has started the decoder shutdown process, and has - // dropped its reference to the decoder. This enables the state machine to - // keep using the decoder's monitor until the state machine has finished - // shutting down, without fear of the monitor being destroyed. After - // shutting down, the state machine will then release this reference, - // causing the decoder to be destroyed. This is accessed on the decode, - // state machine, audio and main threads. - RefPtr mDecoder; + void* const mDecoderID; + const RefPtr mFrameStats; + const RefPtr mVideoFrameContainer; + const dom::AudioChannel mAudioChannel; // Task queue for running the state machine. RefPtr mTaskQueue; @@ -1186,10 +1206,26 @@ private: MediaEventListener mAudioQueueListener; MediaEventListener mVideoQueueListener; + MediaEventProducerExc, + nsAutoPtr, + MediaDecoderEventVisibility> mMetadataLoadedEvent; + MediaEventProducerExc, + MediaDecoderEventVisibility> mFirstFrameLoadedEvent; + + MediaEventProducer mOnPlaybackEvent; + MediaEventProducer mOnSeekingStart; + // True if audio is offloading. // Playback will not start when audio is offloading. bool mAudioOffloading; +#ifdef MOZ_EME + void OnCDMProxyReady(RefPtr aProxy); + void OnCDMProxyNotReady(); + RefPtr mCDMProxy; + MozPromiseRequestHolder mCDMProxyPromise; +#endif + private: // The buffered range. Mirrored from the decoder thread. Mirror mBuffered; diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 904eb3df6d..b29a3de4e5 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -18,6 +18,10 @@ #include +#ifdef MOZ_EME +#include "mozilla/CDMProxy.h" +#endif + using namespace mozilla::media; using mozilla::layers::Image; @@ -63,7 +67,6 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, , mLastReportedNumDecodedFrames(0) , mLayersBackendType(aLayersBackend) , mInitDone(false) - , mSeekable(false) , mIsEncrypted(false) , mTrackDemuxersMayBlock(false) , mHardwareAccelerationDisabled(false) @@ -187,11 +190,54 @@ MediaFormatReader::Init() return NS_OK; } +#ifdef MOZ_EME +class DispatchKeyNeededEvent : public nsRunnable { +public: + DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder, + nsTArray& aInitData, + const nsString& aInitDataType) + : mDecoder(aDecoder) + , mInitData(aInitData) + , mInitDataType(aInitDataType) + { + } + NS_IMETHOD Run() { + // Note: Null check the owner, as the decoder could have been shutdown + // since this event was dispatched. + MediaDecoderOwner* owner = mDecoder->GetOwner(); + if (owner) { + owner->DispatchEncrypted(mInitData, mInitDataType); + } + mDecoder = nullptr; + return NS_OK; + } +private: + RefPtr mDecoder; + nsTArray mInitData; + nsString mInitDataType; +}; + +void +MediaFormatReader::SetCDMProxy(CDMProxy* aProxy) +{ + RefPtr proxy = aProxy; + RefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([=] () { + MOZ_ASSERT(self->OnTaskQueue()); + self->mCDMProxy = proxy; + }); + OwnerThread()->Dispatch(r.forget()); +} +#endif // MOZ_EME + bool MediaFormatReader::IsWaitingOnCDMResource() { MOZ_ASSERT(OnTaskQueue()); - // EME Stub +#ifdef MOZ_EME + return IsEncrypted() && !mCDMProxy; +#else return false; +#endif } RefPtr @@ -261,6 +307,13 @@ MediaFormatReader::OnDemuxerInitDone(nsresult) mIsEncrypted = crypto && crypto->IsEncrypted(); if (mDecoder && crypto && crypto->IsEncrypted()) { +#ifdef MOZ_EME + // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING. + for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) { + NS_DispatchToMainThread( + new DispatchKeyNeededEvent(mDecoder, crypto->mInitDatas[i].mInitData, NS_LITERAL_STRING("cenc"))); + } +#endif // MOZ_EME mInfo.mCrypto = *crypto; } @@ -272,7 +325,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult) mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(duration)); } - mSeekable = mDemuxer->IsSeekable(); + mInfo.mMediaSeekable = mDemuxer->IsSeekable(); if (!videoActive && !audioActive) { mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); @@ -302,8 +355,13 @@ MediaFormatReader::EnsureDecodersCreated() mPlatform = new PDMFactory(); NS_ENSURE_TRUE(mPlatform, false); if (IsEncrypted()) { +#ifdef MOZ_EME + MOZ_ASSERT(mCDMProxy); + mPlatform->SetCDMProxy(mCDMProxy); +#else // EME not supported. return false; +#endif } } @@ -446,6 +504,14 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe, if (ShouldSkip(aSkipToNextKeyframe, timeThreshold)) { // Cancel any pending demux request. mVideo.mDemuxRequest.DisconnectIfExists(); + + // I think it's still possible for an output to have been sent from the decoder + // and is currently sitting in our event queue waiting to be processed. The following + // flush won't clear it, and when we return to the event loop it'll be added to our + // output queue and be used. + // This code will count that as dropped, which was the intent, but not quite true. + mDecoder->NotifyDecodedFrames(0, 0, SizeOfVideoQueueInFrames()); + Flush(TrackInfo::kVideoTrack); RefPtr p = mVideo.mPromise.Ensure(__func__); SkipVideoDemuxToNextKeyFrame(timeThreshold); @@ -1268,7 +1334,7 @@ MediaFormatReader::Seek(int64_t aTime, int64_t aUnused) MOZ_DIAGNOSTIC_ASSERT(mVideo.mTimeThreshold.isNothing()); MOZ_DIAGNOSTIC_ASSERT(mAudio.mTimeThreshold.isNothing()); - if (!mSeekable) { + if (!mInfo.mMediaSeekable) { LOG("Seek() END (Unseekable)"); return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 5b3df155fb..98dccf05e2 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -17,6 +17,8 @@ namespace mozilla { +class CDMProxy; + class MediaFormatReader final : public MediaDecoderReader { typedef TrackInfo::TrackType TrackType; @@ -47,11 +49,6 @@ public: RefPtr Seek(int64_t aTime, int64_t aUnused) override; - bool IsMediaSeekable() override - { - return mSeekable; - } - protected: void NotifyDataArrivedInternal() override; @@ -95,6 +92,10 @@ public: return mTrackDemuxersMayBlock; } +#ifdef MOZ_EME + void SetCDMProxy(CDMProxy* aProxy) override; +#endif + private: bool HasVideo() { return mVideo.mTrackDemuxer; } bool HasAudio() { return mAudio.mTrackDemuxer; } @@ -390,9 +391,6 @@ private: // True if we've read the streams' metadata. bool mInitDone; MozPromiseHolder mMetadataPromise; - // Accessed from multiple thread, in particular the MediaDecoderStateMachine, - // however the value doesn't change after reading the metadata. - bool mSeekable; bool IsEncrypted() { return mIsEncrypted; @@ -432,6 +430,10 @@ private: RefPtr mVideoFrameContainer; layers::ImageContainer* GetImageContainer(); +#ifdef MOZ_EME + RefPtr mCDMProxy; +#endif + #if defined(READER_DORMANT_HEURISTIC) const bool mDormantEnabled; #endif diff --git a/dom/media/MediaInfo.h b/dom/media/MediaInfo.h index 0c76e674c4..0cf07adc50 100644 --- a/dom/media/MediaInfo.h +++ b/dom/media/MediaInfo.h @@ -427,6 +427,9 @@ public: // a duration until we know the start time, so we need to track it separately. media::NullableTimeUnit mUnadjustedMetadataEndTime; + // True if the media is seekable (i.e. supports random access). + bool mMediaSeekable = true; + EncryptionInfo mCrypto; }; diff --git a/dom/media/MediaQueue.h b/dom/media/MediaQueue.h index 712f700e8b..740d6b21ae 100644 --- a/dom/media/MediaQueue.h +++ b/dom/media/MediaQueue.h @@ -46,7 +46,7 @@ public: MOZ_ASSERT(aItem); NS_ADDREF(aItem); nsDeque::Push(aItem); - mPushEvent.Notify(); + mPushEvent.Notify(RefPtr(aItem)); } inline void PushFront(T* aItem) { @@ -54,7 +54,7 @@ public: MOZ_ASSERT(aItem); NS_ADDREF(aItem); nsDeque::PushFront(aItem); - mPushEvent.Notify(); + mPushEvent.Notify(RefPtr(aItem)); } inline already_AddRefed PopFront() { @@ -161,7 +161,7 @@ public: return mPopEvent; } - MediaEventSource& PushEvent() { + MediaEventSource>& PushEvent() { return mPushEvent; } @@ -172,7 +172,7 @@ public: private: mutable ReentrantMonitor mReentrantMonitor; MediaEventProducer> mPopEvent; - MediaEventProducer mPushEvent; + MediaEventProducer> mPushEvent; MediaEventProducer mFinishEvent; // True when we've decoded the last frame of data in the // bitstream for which we're queueing frame data. diff --git a/dom/media/VideoFrameContainer.h b/dom/media/VideoFrameContainer.h index 40bacec38e..7c07299b03 100644 --- a/dom/media/VideoFrameContainer.h +++ b/dom/media/VideoFrameContainer.h @@ -78,6 +78,8 @@ public: B2G_ACL_EXPORT ImageContainer* GetImageContainer(); void ForgetElement() { mElement = nullptr; } + uint32_t GetDroppedImageCount() { return mImageContainer->GetDroppedImageCount(); } + protected: void SetCurrentFramesLocked(const gfx::IntSize& aIntrinsicSize, const nsTArray& aImages); diff --git a/dom/media/android/AndroidMediaReader.h b/dom/media/android/AndroidMediaReader.h index 9e43b817e2..267aca3a31 100644 --- a/dom/media/android/AndroidMediaReader.h +++ b/dom/media/android/AndroidMediaReader.h @@ -48,12 +48,6 @@ public: virtual bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold); - virtual bool IsMediaSeekable() - { - // not used - return true; - } - virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags); virtual RefPtr diff --git a/dom/media/compiledtest/TestAudioPacketizer.cpp b/dom/media/compiledtest/TestAudioPacketizer.cpp index 08bf94e012..c223ba6754 100644 --- a/dom/media/compiledtest/TestAudioPacketizer.cpp +++ b/dom/media/compiledtest/TestAudioPacketizer.cpp @@ -5,7 +5,6 @@ #include #include -#define _USE_MATH_DEFINES #include #include "../AudioPacketizer.h" diff --git a/dom/media/directshow/DirectShowReader.cpp b/dom/media/directshow/DirectShowReader.cpp index 9e3372e86c..8b60fe8f46 100644 --- a/dom/media/directshow/DirectShowReader.cpp +++ b/dom/media/directshow/DirectShowReader.cpp @@ -190,6 +190,7 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo, DWORD seekCaps = 0; hr = mMediaSeeking->GetCapabilities(&seekCaps); + mInfo.mMediaSeekable = SUCCEEDED(hr) && (AM_SEEKING_CanSeekAbsolute & seekCaps); int64_t duration = mMP3FrameParser.GetDuration(); if (SUCCEEDED(hr)) { @@ -210,15 +211,6 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo, return NS_OK; } -bool -DirectShowReader::IsMediaSeekable() -{ - DWORD seekCaps = 0; - HRESULT hr = mMediaSeeking->GetCapabilities(&seekCaps); - return ((AM_SEEKING_CanSeekAbsolute & seekCaps) == - AM_SEEKING_CanSeekAbsolute); -} - inline float UnsignedByteToAudioSample(uint8_t aValue) { diff --git a/dom/media/directshow/DirectShowReader.h b/dom/media/directshow/DirectShowReader.h index 50ea70b599..5e80c98261 100644 --- a/dom/media/directshow/DirectShowReader.h +++ b/dom/media/directshow/DirectShowReader.h @@ -56,12 +56,8 @@ public: protected: void NotifyDataArrivedInternal() override; -public: - - bool IsMediaSeekable() override; private: - // Notifies the filter graph that playback is complete. aStatus is // the code to send to the filter graph. Always returns false, so // that we can just "return Finish()" from DecodeAudioData(). diff --git a/dom/media/gstreamer/GStreamerReader.cpp b/dom/media/gstreamer/GStreamerReader.cpp index 431a5188ab..d7eeb1a3ab 100644 --- a/dom/media/gstreamer/GStreamerReader.cpp +++ b/dom/media/gstreamer/GStreamerReader.cpp @@ -468,12 +468,15 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, /* report the duration */ gint64 duration; + bool isMediaSeekable = false; + if (isMP3 && mMP3FrameParser.IsMP3()) { // The MP3FrameParser has reported a duration; use that over the gstreamer // reported duration for inter-platform consistency. mUseParserDuration = true; mLastParserDuration = mMP3FrameParser.GetDuration(); mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mLastParserDuration)); + isMediaSeekable = true; } else { LOG(LogLevel::Debug, "querying duration"); // Otherwise use the gstreamer duration. @@ -488,9 +491,12 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, LOG(LogLevel::Debug, "have duration %" GST_TIME_FORMAT, GST_TIME_ARGS(duration)); duration = GST_TIME_AS_USECONDS (duration); mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration)); + isMediaSeekable = true; } } + mInfo.mMediaSeekable = isMediaSeekable; + int n_video = 0, n_audio = 0; g_object_get(mPlayBin, "n-video", &n_video, "n-audio", &n_audio, nullptr); @@ -518,28 +524,6 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, return NS_OK; } -bool -GStreamerReader::IsMediaSeekable() -{ - if (mUseParserDuration) { - return true; - } - - gint64 duration; -#if GST_VERSION_MAJOR >= 1 - if (gst_element_query_duration(GST_ELEMENT(mPlayBin), GST_FORMAT_TIME, - &duration)) { -#else - GstFormat format = GST_FORMAT_TIME; - if (gst_element_query_duration(GST_ELEMENT(mPlayBin), &format, &duration) && - format == GST_FORMAT_TIME) { -#endif - return true; - } - - return false; -} - nsresult GStreamerReader::CheckSupportedFormats() { bool done = false; diff --git a/dom/media/gstreamer/GStreamerReader.h b/dom/media/gstreamer/GStreamerReader.h index 01e5320ac1..3738b57749 100644 --- a/dom/media/gstreamer/GStreamerReader.h +++ b/dom/media/gstreamer/GStreamerReader.h @@ -54,11 +54,10 @@ public: protected: virtual void NotifyDataArrivedInternal() override; + public: layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); } - virtual bool IsMediaSeekable() override; - private: bool HasAudio() { return mInfo.HasAudio(); } bool HasVideo() { return mInfo.HasVideo(); } diff --git a/dom/media/mediasink/DecodedStream.cpp b/dom/media/mediasink/DecodedStream.cpp index 9041b80de7..456666b776 100644 --- a/dom/media/mediasink/DecodedStream.cpp +++ b/dom/media/mediasink/DecodedStream.cpp @@ -201,44 +201,9 @@ DecodedStreamData::SetPlaying(bool aPlaying) } } -class OutputStreamListener : public MediaStreamListener { - typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent; -public: - explicit OutputStreamListener(OutputStreamData* aOwner) : mOwner(aOwner) {} - - void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override - { - if (event == EVENT_FINISHED) { - nsCOMPtr r = NS_NewRunnableMethod( - this, &OutputStreamListener::DoNotifyFinished); - aGraph->DispatchToMainThreadAfterStreamStateUpdate(r.forget()); - } - } - - void Forget() - { - MOZ_ASSERT(NS_IsMainThread()); - mOwner = nullptr; - } - -private: - void DoNotifyFinished() - { - MOZ_ASSERT(NS_IsMainThread()); - if (mOwner) { - // Remove the finished stream so it won't block the decoded stream. - mOwner->Remove(); - } - } - - // Main thread only - OutputStreamData* mOwner; -}; - OutputStreamData::~OutputStreamData() { MOZ_ASSERT(NS_IsMainThread()); - mListener->Forget(); // Break the connection to the input stream if necessary. if (mPort) { mPort->Destroy(); @@ -250,8 +215,6 @@ OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStrea { mOwner = aOwner; mStream = aStream; - mListener = new OutputStreamListener(this); - aStream->AddListener(mListener); } void @@ -284,13 +247,6 @@ OutputStreamData::Disconnect() return true; } -void -OutputStreamData::Remove() -{ - MOZ_ASSERT(NS_IsMainThread()); - mOwner->Remove(mStream); -} - MediaStreamGraph* OutputStreamData::Graph() const { @@ -499,8 +455,7 @@ DecodedStream::CreateData(MozPromiseHolder&& aPromise) // No need to create a source stream when there are no output streams. This // happens when RemoveOutput() is called immediately after StartPlayback(). - // Also we don't create a source stream when MDSM has begun shutdown. - if (!mOutputStreamManager.Graph() || mShuttingDown) { + if (!mOutputStreamManager.Graph()) { // Resolve the promise to indicate the end of playback. aPromise.Resolve(true, __func__); return; @@ -521,16 +476,30 @@ DecodedStream::CreateData(MozPromiseHolder&& aPromise) return NS_OK; } private: + virtual ~R() + { + // mData is not transferred when dispatch fails and Run() is not called. + // We need to dispatch a task to ensure DecodedStreamData is destroyed + // properly on the main thread. + if (mData) { + DecodedStreamData* data = mData.release(); + RefPtr self = mThis.forget(); + nsCOMPtr r = NS_NewRunnableFunction([=] () { + self->mOutputStreamManager.Disconnect(); + delete data; + }); + AbstractThread::MainThread()->Dispatch(r.forget()); + } + } RefPtr mThis; Method mMethod; UniquePtr mData; }; // Post a message to ensure |mData| is only updated on the worker thread. - // Note this must be done before MDSM's shutdown since dispatch could fail - // when the worker thread is shut down. + // Note this could fail when MDSM begin to shut down the worker thread. nsCOMPtr r = new R(this, &DecodedStream::OnDataCreated, data); - mOwnerThread->Dispatch(r.forget()); + mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess); } bool @@ -649,6 +618,9 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime, MediaData* aData, AudioSegment* aOutput, uint32_t aRate, double aVolume) { + // The amount of audio frames that is used to fuzz rounding errors. + static const int64_t AUDIO_FUZZ_FRAMES = 1; + MOZ_ASSERT(aData); AudioData* audio = aData->As(); // This logic has to mimic AudioSink closely to make sure we write @@ -660,11 +632,11 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime, if (!audioWrittenOffset.isValid() || !frameOffset.isValid() || // ignore packet that we've already processed - frameOffset.value() + audio->mFrames <= audioWrittenOffset.value()) { + audio->GetEndTime() <= aStream->mNextAudioTime) { return; } - if (audioWrittenOffset.value() < frameOffset.value()) { + if (audioWrittenOffset.value() + AUDIO_FUZZ_FRAMES < frameOffset.value()) { int64_t silentFrames = frameOffset.value() - audioWrittenOffset.value(); // Write silence to catch up AudioSegment silence; @@ -674,20 +646,17 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime, aOutput->AppendFrom(&silence); } - MOZ_ASSERT(audioWrittenOffset.value() >= frameOffset.value()); - - int64_t offset = audioWrittenOffset.value() - frameOffset.value(); - size_t framesToWrite = audio->mFrames - offset; - + // Always write the whole sample without truncation to be consistent with + // DecodedAudioDataSink::PlayFromAudioQueue() audio->EnsureAudioBuffer(); RefPtr buffer = audio->mAudioBuffer; AudioDataValue* bufferData = static_cast(buffer->Data()); nsAutoTArray channels; for (uint32_t i = 0; i < audio->mChannels; ++i) { - channels.AppendElement(bufferData + i * audio->mFrames + offset); + channels.AppendElement(bufferData + i * audio->mFrames); } - aOutput->AppendFrames(buffer.forget(), channels, framesToWrite); - aStream->mAudioFramesWritten += framesToWrite; + aOutput->AppendFrames(buffer.forget(), channels, audio->mFrames); + aStream->mAudioFramesWritten += audio->mFrames; aOutput->ApplyVolume(aVolume); aStream->mNextAudioTime = audio->GetEndTime(); diff --git a/dom/media/mediasink/DecodedStream.h b/dom/media/mediasink/DecodedStream.h index e8978e828f..5b384e3012 100644 --- a/dom/media/mediasink/DecodedStream.h +++ b/dom/media/mediasink/DecodedStream.h @@ -26,17 +26,12 @@ class MediaData; class MediaInputPort; class MediaStream; class MediaStreamGraph; -class OutputStreamListener; class OutputStreamManager; class ProcessedMediaStream; class TimeStamp; template class MediaQueue; -namespace layers { -class Image; -} // namespace layers - class OutputStreamData { public: ~OutputStreamData(); @@ -47,9 +42,6 @@ public: // Disconnect mStream from its input stream. // Return false is mStream is already destroyed, otherwise true. bool Disconnect(); - // Called by OutputStreamListener to remove self from the output streams - // managed by OutputStreamManager. - void Remove(); // Return true if aStream points to the same object as mStream. // Used by OutputStreamManager to remove an output stream. bool Equals(MediaStream* aStream) @@ -64,7 +56,6 @@ private: RefPtr mStream; // mPort connects our mStream to an input stream. RefPtr mPort; - RefPtr mListener; }; class OutputStreamManager { diff --git a/dom/media/mediasink/VideoSink.cpp b/dom/media/mediasink/VideoSink.cpp index 2b673dd670..112686ee5c 100644 --- a/dom/media/mediasink/VideoSink.cpp +++ b/dom/media/mediasink/VideoSink.cpp @@ -26,7 +26,6 @@ VideoSink::VideoSink(AbstractThread* aThread, VideoFrameContainer* aContainer, bool aRealTime, FrameStatistics& aFrameStats, - int aDelayDuration, uint32_t aVQueueSentToCompositerSize) : mOwnerThread(aThread) , mAudioSink(aAudioSink) @@ -36,9 +35,9 @@ VideoSink::VideoSink(AbstractThread* aThread, , mRealTime(aRealTime) , mFrameStats(aFrameStats) , mVideoFrameEndTime(-1) + , mOldDroppedCount(0) , mHasVideo(false) , mUpdateScheduler(aThread) - , mDelayDuration(aDelayDuration) , mVideoQueueSendToCompositorSize(aVQueueSentToCompositerSize) { MOZ_ASSERT(mAudioSink, "AudioSink should exist."); @@ -218,12 +217,18 @@ VideoSink::Shutdown() } void -VideoSink::OnVideoQueueEvent() +VideoSink::OnVideoQueueEvent(RefPtr&& aSample) { AssertOwnerThread(); // Listen to push event, VideoSink should try rendering ASAP if first frame // arrives but update scheduler is not triggered yet. - TryUpdateRenderedVideoFrames(); + VideoData* v = aSample->As(); + if (!v->mSentToCompositor) { + // Since we push rendered frames back to the queue, we will receive + // push events for them. We only need to trigger render loop + // when this frame is not rendered yet. + TryUpdateRenderedVideoFrames(); + } } void @@ -323,6 +328,10 @@ VideoSink::RenderVideoFrames(int32_t aMaxFrames, frame->mTime, frame->mFrameID, VideoQueue().GetSize()); } mContainer->SetCurrentFrames(frames[0]->As()->mDisplay, images); + + uint32_t dropped = mContainer->GetDroppedImageCount(); + mFrameStats.NotifyDecodedFrames(0, 0, dropped - mOldDroppedCount); + mOldDroppedCount = dropped; } void @@ -338,7 +347,7 @@ VideoSink::UpdateRenderedVideoFrames() // the current frame. NS_ASSERTION(clockTime >= 0, "Should have positive clock time."); - int64_t remainingTime = mDelayDuration; + int64_t remainingTime = -1; if (VideoQueue().GetSize() > 0) { RefPtr currentFrame = VideoQueue().PopFront(); int32_t framesRemoved = 0; @@ -365,7 +374,14 @@ VideoSink::UpdateRenderedVideoFrames() RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime); - TimeStamp target = nowTime + TimeDuration::FromMicroseconds(remainingTime); + // No next fame to render. There is no need to schedule next render + // loop. We will run render loops again upon incoming frames. + if (remainingTime < 0) { + return; + } + + TimeStamp target = nowTime + TimeDuration::FromMicroseconds( + remainingTime / mAudioSink->GetPlaybackParams().mPlaybackRate); RefPtr self = this; mUpdateScheduler.Ensure(target, [self] () { diff --git a/dom/media/mediasink/VideoSink.h b/dom/media/mediasink/VideoSink.h index f57560b232..c7c405d0c9 100644 --- a/dom/media/mediasink/VideoSink.h +++ b/dom/media/mediasink/VideoSink.h @@ -35,7 +35,6 @@ public: VideoFrameContainer* aContainer, bool aRealTime, FrameStatistics& aFrameStats, - int aDelayDuration, uint32_t aVQueueSentToCompositerSize); const PlaybackParams& GetPlaybackParams() const override; @@ -74,7 +73,7 @@ private: virtual ~VideoSink(); // VideoQueue listener related. - void OnVideoQueueEvent(); + void OnVideoQueueEvent(RefPtr&& aSample); void ConnectListener(); void DisconnectListener(); @@ -130,6 +129,8 @@ private: // in microseconds. int64_t mVideoFrameEndTime; + uint32_t mOldDroppedCount; + // Event listeners for VideoQueue MediaEventListener mPushListener; @@ -139,10 +140,6 @@ private: // Used to trigger another update of rendered frames in next round. DelayedScheduler mUpdateScheduler; - // A delay duration to trigger next time UpdateRenderedVideoFrames(). - // Based on the default value in MDSM. - const int mDelayDuration; - // Max frame number sent to compositor at a time. // Based on the pref value obtained in MDSM. const uint32_t mVideoQueueSendToCompositorSize; diff --git a/dom/media/ogg/OggReader.cpp b/dom/media/ogg/OggReader.cpp index f0f379a555..b8327eea1a 100644 --- a/dom/media/ogg/OggReader.cpp +++ b/dom/media/ogg/OggReader.cpp @@ -486,20 +486,17 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo, } else { return NS_ERROR_FAILURE; } + + { + ReentrantMonitorAutoEnter mon(mMonitor); + mInfo.mMediaSeekable = !mIsChained; + } + *aInfo = mInfo; return NS_OK; } -bool -OggReader::IsMediaSeekable() -{ - if (mIsChained) { - return false; - } - return true; -} - nsresult OggReader::DecodeVorbis(ogg_packet* aPacket) { NS_ASSERTION(aPacket->granulepos != -1, "Must know vorbis granulepos!"); @@ -712,7 +709,7 @@ void OggReader::SetChained(bool aIsChained) { ReentrantMonitorAutoEnter mon(mMonitor); mIsChained = aIsChained; } - mDecoder->DispatchSetMediaSeekable(false); + mOnMediaNotSeekable.Notify(); } bool OggReader::ReadOggChain() diff --git a/dom/media/ogg/OggReader.h b/dom/media/ogg/OggReader.h index bb715d4ced..cc34c163a1 100644 --- a/dom/media/ogg/OggReader.h +++ b/dom/media/ogg/OggReader.h @@ -66,8 +66,6 @@ public: Seek(int64_t aTime, int64_t aEndTime) override; virtual media::TimeIntervals GetBuffered() override; - virtual bool IsMediaSeekable() override; - private: bool HasAudio() { return (mVorbisState != 0 && mVorbisState->mActive) || diff --git a/dom/media/omx/MediaOmxCommonDecoder.cpp b/dom/media/omx/MediaOmxCommonDecoder.cpp index c25e1b143e..6d1b77f0e3 100644 --- a/dom/media/omx/MediaOmxCommonDecoder.cpp +++ b/dom/media/omx/MediaOmxCommonDecoder.cpp @@ -80,10 +80,6 @@ MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr aInfo, { MOZ_ASSERT(NS_IsMainThread()); - if (mShuttingDown) { - return; - } - MediaDecoder::FirstFrameLoaded(aInfo, aEventVisibility); if (!CheckDecoderCanOffloadAudio()) { diff --git a/dom/media/omx/MediaOmxReader.cpp b/dom/media/omx/MediaOmxReader.cpp index 9c765b9de8..630b9302a6 100644 --- a/dom/media/omx/MediaOmxReader.cpp +++ b/dom/media/omx/MediaOmxReader.cpp @@ -311,6 +311,8 @@ void MediaOmxReader::HandleResourceAllocated() mInfo.mAudio.mRate = sampleRate; } + mInfo.mMediaSeekable = mExtractor->flags() & MediaExtractor::CAN_SEEK; + RefPtr metadata = new MetadataHolder(); metadata->mInfo = mInfo; metadata->mTags = nullptr; @@ -322,13 +324,6 @@ void MediaOmxReader::HandleResourceAllocated() mMetadataPromise.Resolve(metadata, __func__); } -bool -MediaOmxReader::IsMediaSeekable() -{ - // Check the MediaExtract flag if the source is seekable. - return (mExtractor->flags() & MediaExtractor::CAN_SEEK); -} - bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) { diff --git a/dom/media/omx/MediaOmxReader.h b/dom/media/omx/MediaOmxReader.h index 2a374c2bda..7151aae9f0 100644 --- a/dom/media/omx/MediaOmxReader.h +++ b/dom/media/omx/MediaOmxReader.h @@ -92,8 +92,6 @@ public: virtual RefPtr Seek(int64_t aTime, int64_t aEndTime) override; - virtual bool IsMediaSeekable() override; - virtual void SetIdle() override; virtual RefPtr Shutdown() override; diff --git a/dom/media/raw/RawReader.cpp b/dom/media/raw/RawReader.cpp index 3157cf727e..6827e9380b 100644 --- a/dom/media/raw/RawReader.cpp +++ b/dom/media/raw/RawReader.cpp @@ -102,13 +102,6 @@ nsresult RawReader::ReadMetadata(MediaInfo* aInfo, return NS_OK; } -bool -RawReader::IsMediaSeekable() -{ - // not used - return true; -} - bool RawReader::DecodeAudioData() { MOZ_ASSERT(OnTaskQueue()); diff --git a/dom/media/raw/RawReader.h b/dom/media/raw/RawReader.h index d4ff7f72e7..de3d8e1043 100644 --- a/dom/media/raw/RawReader.h +++ b/dom/media/raw/RawReader.h @@ -33,8 +33,6 @@ public: virtual media::TimeIntervals GetBuffered() override; - virtual bool IsMediaSeekable() override; - private: bool ReadFromResource(uint8_t *aBuf, uint32_t aLength); diff --git a/dom/media/wave/WaveReader.cpp b/dom/media/wave/WaveReader.cpp index f9f4ffc5c8..80b783b6d4 100644 --- a/dom/media/wave/WaveReader.cpp +++ b/dom/media/wave/WaveReader.cpp @@ -145,13 +145,6 @@ nsresult WaveReader::ReadMetadata(MediaInfo* aInfo, return NS_OK; } -bool -WaveReader::IsMediaSeekable() -{ - // not used - return true; -} - template T UnsignedByteToAudioSample(uint8_t aValue); template T SignedShortToAudioSample(int16_t aValue); diff --git a/dom/media/wave/WaveReader.h b/dom/media/wave/WaveReader.h index cc9708152d..754192fa83 100644 --- a/dom/media/wave/WaveReader.h +++ b/dom/media/wave/WaveReader.h @@ -33,8 +33,6 @@ public: virtual media::TimeIntervals GetBuffered() override; - virtual bool IsMediaSeekable() override; - private: bool ReadAll(char* aBuf, int64_t aSize, int64_t* aBytesRead = nullptr); bool LoadRIFFChunk(); diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp index 5f03e10817..34bbbcbbe1 100644 --- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -520,8 +520,7 @@ AudioContext::CreatePeriodicWave(const Float32Array& aRealData, aImagData.ComputeLengthAndData(); if (aRealData.Length() != aImagData.Length() || - aRealData.Length() == 0 || - aRealData.Length() > 4096) { + aRealData.Length() == 0) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } diff --git a/dom/media/webaudio/BufferDecoder.cpp b/dom/media/webaudio/BufferDecoder.cpp index e863f60d5e..1a07903713 100644 --- a/dom/media/webaudio/BufferDecoder.cpp +++ b/dom/media/webaudio/BufferDecoder.cpp @@ -39,12 +39,6 @@ BufferDecoder::GetResource() const return mResource; } -void -BufferDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) -{ - // ignore -} - void BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded, uint32_t aDropped) @@ -52,12 +46,6 @@ BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded, // ignore } -void -BufferDecoder::SetMediaSeekable(bool aMediaSeekable) -{ - // ignore -} - VideoFrameContainer* BufferDecoder::GetVideoFrameContainer() { @@ -72,36 +60,6 @@ BufferDecoder::GetImageContainer() return nullptr; } -bool -BufferDecoder::IsTransportSeekable() -{ - return false; -} - -bool -BufferDecoder::IsMediaSeekable() -{ - return false; -} - -void -BufferDecoder::MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags, MediaDecoderEventVisibility aEventVisibility) -{ - // ignore -} - -void -BufferDecoder::FirstFrameLoaded(nsAutoPtr aInfo, MediaDecoderEventVisibility aEventVisibility) -{ - // ignore -} - -void -BufferDecoder::OnReadMetadataCompleted() -{ - // ignore -} - MediaDecoderOwner* BufferDecoder::GetOwner() { diff --git a/dom/media/webaudio/BufferDecoder.h b/dom/media/webaudio/BufferDecoder.h index 5277627451..f12a032e58 100644 --- a/dom/media/webaudio/BufferDecoder.h +++ b/dom/media/webaudio/BufferDecoder.h @@ -33,32 +33,14 @@ public: virtual MediaResource* GetResource() const final override; - virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override; - virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded, uint32_t aDropped) final override; - virtual void SetMediaSeekable(bool aMediaSeekable) final override; - virtual VideoFrameContainer* GetVideoFrameContainer() final override; virtual layers::ImageContainer* GetImageContainer() final override; - virtual bool IsTransportSeekable() final override; - - virtual bool IsMediaSeekable() final override; - - virtual void MetadataLoaded(nsAutoPtr aInfo, - nsAutoPtr aTags, - MediaDecoderEventVisibility aEventVisibility) final override; - virtual void FirstFrameLoaded(nsAutoPtr aInfo, - MediaDecoderEventVisibility aEventVisibility) final override; - - virtual void OnReadMetadataCompleted() final override; - virtual MediaDecoderOwner* GetOwner() final override; - virtual void NotifyDataArrived() final override {}; - private: virtual ~BufferDecoder(); RefPtr mTaskQueueIdentity; diff --git a/dom/media/webaudio/ConvolverNode.cpp b/dom/media/webaudio/ConvolverNode.cpp index 81456cbf34..094a4acc3b 100644 --- a/dom/media/webaudio/ConvolverNode.cpp +++ b/dom/media/webaudio/ConvolverNode.cpp @@ -96,7 +96,6 @@ public: } mReverb = new WebCore::Reverb(mBuffer, mBufferLength, - WEBAUDIO_BLOCK_SIZE, MaxFFTSize, 2, mUseBackgroundThreads, mNormalize, mSampleRate); } @@ -153,7 +152,7 @@ public: } aOutput->AllocateChannels(2); - mReverb->process(&input, aOutput, WEBAUDIO_BLOCK_SIZE); + mReverb->process(&input, aOutput); } virtual bool IsActive() const override diff --git a/dom/media/webaudio/FFTBlock.h b/dom/media/webaudio/FFTBlock.h index d61356c235..afd5ba7947 100644 --- a/dom/media/webaudio/FFTBlock.h +++ b/dom/media/webaudio/FFTBlock.h @@ -15,7 +15,17 @@ #include "AlignedTArray.h" #include "AudioNodeEngine.h" +#if defined(MOZ_LIBAV_FFT) +#ifdef __cplusplus +extern "C" { +#endif +#include "libavcodec/avfft.h" +#ifdef __cplusplus +} +#endif +#else #include "kiss_fft/kiss_fftr.h" +#endif namespace mozilla { @@ -25,7 +35,9 @@ namespace mozilla { class FFTBlock final { union ComplexU { +#if !defined(MOZ_LIBAV_FFT) kiss_fft_cpx c; +#endif float f[2]; struct { float r; @@ -35,11 +47,16 @@ class FFTBlock final public: explicit FFTBlock(uint32_t aFFTSize) +#if defined(MOZ_LIBAV_FFT) + : mAvRDFT(nullptr) + , mAvIRDFT(nullptr) +#else : mKissFFT(nullptr) , mKissIFFT(nullptr) #ifdef BUILD_ARM_NEON , mOmxFFT(nullptr) , mOmxIFFT(nullptr) +#endif #endif { MOZ_COUNT_CTOR(FFTBlock); @@ -61,6 +78,13 @@ public: void PerformFFT(const float* aData) { EnsureFFT(); +#if defined(MOZ_LIBAV_FFT) + PodCopy(mOutputBuffer.Elements()->f, aData, mFFTSize); + av_rdft_calc(mAvRDFT, mOutputBuffer.Elements()->f); + // Recover packed Nyquist. + mOutputBuffer[mFFTSize / 2].r = mOutputBuffer[0].i; + mOutputBuffer[0].i = 0.0f; +#else #ifdef BUILD_ARM_NEON if (mozilla::supports_neon()) { omxSP_FFTFwd_RToCCS_F32_Sfs(aData, mOutputBuffer.Elements()->f, mOmxFFT); @@ -69,6 +93,7 @@ public: { kiss_fftr(mKissFFT, aData, &(mOutputBuffer.Elements()->c)); } +#endif } // Inverse-transform internal data and store the resulting FFTSize() // points in aDataOut. @@ -83,6 +108,17 @@ public: void GetInverseWithoutScaling(float* aDataOut) { EnsureIFFT(); +#if defined(MOZ_LIBAV_FFT) + { + // Even though this function doesn't scale, the libav forward transform + // gives a value that needs scaling by 2 in order for things to turn out + // similar to how we expect from kissfft/openmax. + AudioBufferCopyWithScale(mOutputBuffer.Elements()->f, 2.0f, + aDataOut, mFFTSize); + aDataOut[1] = 2.0f * mOutputBuffer[mFFTSize/2].r; // Packed Nyquist + av_rdft_calc(mAvIRDFT, aDataOut); + } +#else #ifdef BUILD_ARM_NEON if (mozilla::supports_neon()) { omxSP_FFTInv_CCSToR_F32_Sfs(mOutputBuffer.Elements()->f, aDataOut, mOmxIFFT); @@ -94,41 +130,23 @@ public: { kiss_fftri(mKissIFFT, &(mOutputBuffer.Elements()->c), aDataOut); } - } - // Inverse-transform the FFTSize()/2+1 points of data in each - // of aRealDataIn and aImagDataIn and store the resulting - // FFTSize() points in aRealDataOut. - void PerformInverseFFT(float* aRealDataIn, - float *aImagDataIn, - float *aRealDataOut) - { - EnsureIFFT(); - const uint32_t inputSize = mFFTSize / 2 + 1; - AlignedTArray inputBuffer(inputSize); - for (uint32_t i = 0; i < inputSize; ++i) { - inputBuffer[i].r = aRealDataIn[i]; - inputBuffer[i].i = aImagDataIn[i]; - } -#ifdef BUILD_ARM_NEON - if (mozilla::supports_neon()) { - omxSP_FFTInv_CCSToR_F32_Sfs(inputBuffer.Elements()->f, - aRealDataOut, mOmxIFFT); - } else #endif - { - kiss_fftri(mKissIFFT, &(inputBuffer.Elements()->c), aRealDataOut); - for (uint32_t i = 0; i < mFFTSize; ++i) { - aRealDataOut[i] /= mFFTSize; - } - } } void Multiply(const FFTBlock& aFrame) { + uint32_t halfSize = mFFTSize / 2; + // DFTs are not packed. + MOZ_ASSERT(mOutputBuffer[0].i == 0); + MOZ_ASSERT(aFrame.mOutputBuffer[0].i == 0); + BufferComplexMultiply(mOutputBuffer.Elements()->f, aFrame.mOutputBuffer.Elements()->f, mOutputBuffer.Elements()->f, - mFFTSize / 2 + 1); + halfSize); + mOutputBuffer[halfSize].r *= aFrame.mOutputBuffer[halfSize].r; + // This would have been set to NaN if either real component was NaN. + mOutputBuffer[0].i = 0.0f; } // Perform a forward FFT on |aData|, assuming zeros after dataSize samples, @@ -165,16 +183,29 @@ public: { return mOutputBuffer[aIndex].r; } + float& RealData(uint32_t aIndex) + { + return mOutputBuffer[aIndex].r; + } float ImagData(uint32_t aIndex) const { return mOutputBuffer[aIndex].i; } + float& ImagData(uint32_t aIndex) + { + return mOutputBuffer[aIndex].i; + } size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { size_t amount = 0; +#if defined(MOZ_LIBAV_FFT) + amount += aMallocSizeOf(mAvRDFT); + amount += aMallocSizeOf(mAvIRDFT); +#else amount += aMallocSizeOf(mKissFFT); amount += aMallocSizeOf(mKissIFFT); +#endif amount += mOutputBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf); return amount; } @@ -190,6 +221,11 @@ private: void EnsureFFT() { +#if defined(MOZ_LIBAV_FFT) + if (!mAvRDFT) { + mAvRDFT = av_rdft_init(log((double)mFFTSize)/M_LN2, DFT_R2C); + } +#else #ifdef BUILD_ARM_NEON if (mozilla::supports_neon()) { if (!mOmxFFT) { @@ -202,9 +238,15 @@ private: mKissFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr); } } +#endif } void EnsureIFFT() { +#if defined(MOZ_LIBAV_FFT) + if (!mAvIRDFT) { + mAvIRDFT = av_rdft_init(log((double)mFFTSize)/M_LN2, IDFT_C2R); + } +#else #ifdef BUILD_ARM_NEON if (mozilla::supports_neon()) { if (!mOmxIFFT) { @@ -217,6 +259,7 @@ private: mKissIFFT = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr); } } +#endif } #ifdef BUILD_ARM_NEON @@ -240,6 +283,11 @@ private: void Clear() { +#if defined(MOZ_LIBAV_FFT) + av_rdft_end(mAvRDFT); + av_rdft_end(mAvIRDFT); + mAvRDFT = mAvIRDFT = nullptr; +#else #ifdef BUILD_ARM_NEON free(mOmxFFT); free(mOmxIFFT); @@ -248,21 +296,27 @@ private: free(mKissFFT); free(mKissIFFT); mKissFFT = mKissIFFT = nullptr; +#endif } void AddConstantGroupDelay(double sampleFrameDelay); void InterpolateFrequencyComponents(const FFTBlock& block0, const FFTBlock& block1, double interp); - +#if defined(MOZ_LIBAV_FFT) + RDFTContext *mAvRDFT; + RDFTContext *mAvIRDFT; +#else kiss_fftr_cfg mKissFFT; kiss_fftr_cfg mKissIFFT; #ifdef BUILD_ARM_NEON OMXFFTSpec_R_F32* mOmxFFT; OMXFFTSpec_R_F32* mOmxIFFT; +#endif #endif AlignedTArray mOutputBuffer; uint32_t mFFTSize; }; -} + +} // namespace mozilla #endif diff --git a/dom/media/webaudio/PeriodicWave.cpp b/dom/media/webaudio/PeriodicWave.cpp index e5f88299d3..12beaa691a 100644 --- a/dom/media/webaudio/PeriodicWave.cpp +++ b/dom/media/webaudio/PeriodicWave.cpp @@ -27,7 +27,6 @@ PeriodicWave::PeriodicWave(AudioContext* aContext, // Caller should have checked this and thrown. MOZ_ASSERT(aLength > 0); - MOZ_ASSERT(aLength <= 4096); mLength = aLength; // Copy coefficient data. The two arrays share an allocation. diff --git a/dom/media/webaudio/blink/Biquad.cpp b/dom/media/webaudio/blink/Biquad.cpp index a8be73c7cb..38c92ff113 100644 --- a/dom/media/webaudio/blink/Biquad.cpp +++ b/dom/media/webaudio/blink/Biquad.cpp @@ -26,16 +26,11 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// For M_PI -// VS2015 requires to be used; doesn't seem to honor -// _USE_MATH_DEFINES. -#define _USE_MATH_DEFINES -#include - #include "Biquad.h" #include #include +#include namespace WebCore { diff --git a/dom/media/webaudio/blink/DenormalDisabler.h b/dom/media/webaudio/blink/DenormalDisabler.h index 34a56069ab..241220732d 100644 --- a/dom/media/webaudio/blink/DenormalDisabler.h +++ b/dom/media/webaudio/blink/DenormalDisabler.h @@ -25,7 +25,6 @@ #ifndef DenormalDisabler_h #define DenormalDisabler_h -#define _USE_MATH_DEFINES #include #include diff --git a/dom/media/webaudio/blink/DirectConvolver.cpp b/dom/media/webaudio/blink/DirectConvolver.cpp deleted file mode 100644 index 43461ad37e..0000000000 --- a/dom/media/webaudio/blink/DirectConvolver.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2012 Intel 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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. - */ - -#include "DirectConvolver.h" -#include "mozilla/PodOperations.h" - -using namespace mozilla; - -namespace WebCore { - -DirectConvolver::DirectConvolver(size_t inputBlockSize) - : m_inputBlockSize(inputBlockSize) -{ - m_buffer.SetLength(inputBlockSize * 2); - PodZero(m_buffer.Elements(), inputBlockSize * 2); -} - -void DirectConvolver::process(const nsTArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess) -{ - MOZ_ASSERT(framesToProcess == m_inputBlockSize); - if (framesToProcess != m_inputBlockSize) - return; - - // Only support kernelSize <= m_inputBlockSize - size_t kernelSize = convolutionKernel->Length(); - MOZ_ASSERT(kernelSize <= m_inputBlockSize); - if (kernelSize > m_inputBlockSize) - return; - - const float* kernelP = convolutionKernel->Elements(); - - // Sanity check - bool isCopyGood = kernelP && sourceP && destP && m_buffer.Elements(); - MOZ_ASSERT(isCopyGood); - if (!isCopyGood) - return; - - float* inputP = m_buffer.Elements() + m_inputBlockSize; - - // Copy samples to 2nd half of input buffer. - memcpy(inputP, sourceP, sizeof(float) * framesToProcess); - - // FIXME: The macro can be further optimized to avoid pipeline stalls. One possibility is to maintain 4 separate sums and change the macro to CONVOLVE_FOUR_SAMPLES. -#define CONVOLVE_ONE_SAMPLE \ - sum += inputP[i - j] * kernelP[j]; \ - j++; - - size_t i = 0; - while (i < framesToProcess) { - size_t j = 0; - float sum = 0; - - // FIXME: SSE optimization may be applied here. - if (kernelSize == 32) { - CONVOLVE_ONE_SAMPLE // 1 - CONVOLVE_ONE_SAMPLE // 2 - CONVOLVE_ONE_SAMPLE // 3 - CONVOLVE_ONE_SAMPLE // 4 - CONVOLVE_ONE_SAMPLE // 5 - CONVOLVE_ONE_SAMPLE // 6 - CONVOLVE_ONE_SAMPLE // 7 - CONVOLVE_ONE_SAMPLE // 8 - CONVOLVE_ONE_SAMPLE // 9 - CONVOLVE_ONE_SAMPLE // 10 - - CONVOLVE_ONE_SAMPLE // 11 - CONVOLVE_ONE_SAMPLE // 12 - CONVOLVE_ONE_SAMPLE // 13 - CONVOLVE_ONE_SAMPLE // 14 - CONVOLVE_ONE_SAMPLE // 15 - CONVOLVE_ONE_SAMPLE // 16 - CONVOLVE_ONE_SAMPLE // 17 - CONVOLVE_ONE_SAMPLE // 18 - CONVOLVE_ONE_SAMPLE // 19 - CONVOLVE_ONE_SAMPLE // 20 - - CONVOLVE_ONE_SAMPLE // 21 - CONVOLVE_ONE_SAMPLE // 22 - CONVOLVE_ONE_SAMPLE // 23 - CONVOLVE_ONE_SAMPLE // 24 - CONVOLVE_ONE_SAMPLE // 25 - CONVOLVE_ONE_SAMPLE // 26 - CONVOLVE_ONE_SAMPLE // 27 - CONVOLVE_ONE_SAMPLE // 28 - CONVOLVE_ONE_SAMPLE // 29 - CONVOLVE_ONE_SAMPLE // 30 - - CONVOLVE_ONE_SAMPLE // 31 - CONVOLVE_ONE_SAMPLE // 32 - - } else if (kernelSize == 64) { - CONVOLVE_ONE_SAMPLE // 1 - CONVOLVE_ONE_SAMPLE // 2 - CONVOLVE_ONE_SAMPLE // 3 - CONVOLVE_ONE_SAMPLE // 4 - CONVOLVE_ONE_SAMPLE // 5 - CONVOLVE_ONE_SAMPLE // 6 - CONVOLVE_ONE_SAMPLE // 7 - CONVOLVE_ONE_SAMPLE // 8 - CONVOLVE_ONE_SAMPLE // 9 - CONVOLVE_ONE_SAMPLE // 10 - - CONVOLVE_ONE_SAMPLE // 11 - CONVOLVE_ONE_SAMPLE // 12 - CONVOLVE_ONE_SAMPLE // 13 - CONVOLVE_ONE_SAMPLE // 14 - CONVOLVE_ONE_SAMPLE // 15 - CONVOLVE_ONE_SAMPLE // 16 - CONVOLVE_ONE_SAMPLE // 17 - CONVOLVE_ONE_SAMPLE // 18 - CONVOLVE_ONE_SAMPLE // 19 - CONVOLVE_ONE_SAMPLE // 20 - - CONVOLVE_ONE_SAMPLE // 21 - CONVOLVE_ONE_SAMPLE // 22 - CONVOLVE_ONE_SAMPLE // 23 - CONVOLVE_ONE_SAMPLE // 24 - CONVOLVE_ONE_SAMPLE // 25 - CONVOLVE_ONE_SAMPLE // 26 - CONVOLVE_ONE_SAMPLE // 27 - CONVOLVE_ONE_SAMPLE // 28 - CONVOLVE_ONE_SAMPLE // 29 - CONVOLVE_ONE_SAMPLE // 30 - - CONVOLVE_ONE_SAMPLE // 31 - CONVOLVE_ONE_SAMPLE // 32 - CONVOLVE_ONE_SAMPLE // 33 - CONVOLVE_ONE_SAMPLE // 34 - CONVOLVE_ONE_SAMPLE // 35 - CONVOLVE_ONE_SAMPLE // 36 - CONVOLVE_ONE_SAMPLE // 37 - CONVOLVE_ONE_SAMPLE // 38 - CONVOLVE_ONE_SAMPLE // 39 - CONVOLVE_ONE_SAMPLE // 40 - - CONVOLVE_ONE_SAMPLE // 41 - CONVOLVE_ONE_SAMPLE // 42 - CONVOLVE_ONE_SAMPLE // 43 - CONVOLVE_ONE_SAMPLE // 44 - CONVOLVE_ONE_SAMPLE // 45 - CONVOLVE_ONE_SAMPLE // 46 - CONVOLVE_ONE_SAMPLE // 47 - CONVOLVE_ONE_SAMPLE // 48 - CONVOLVE_ONE_SAMPLE // 49 - CONVOLVE_ONE_SAMPLE // 50 - - CONVOLVE_ONE_SAMPLE // 51 - CONVOLVE_ONE_SAMPLE // 52 - CONVOLVE_ONE_SAMPLE // 53 - CONVOLVE_ONE_SAMPLE // 54 - CONVOLVE_ONE_SAMPLE // 55 - CONVOLVE_ONE_SAMPLE // 56 - CONVOLVE_ONE_SAMPLE // 57 - CONVOLVE_ONE_SAMPLE // 58 - CONVOLVE_ONE_SAMPLE // 59 - CONVOLVE_ONE_SAMPLE // 60 - - CONVOLVE_ONE_SAMPLE // 61 - CONVOLVE_ONE_SAMPLE // 62 - CONVOLVE_ONE_SAMPLE // 63 - CONVOLVE_ONE_SAMPLE // 64 - - } else if (kernelSize == 128) { - CONVOLVE_ONE_SAMPLE // 1 - CONVOLVE_ONE_SAMPLE // 2 - CONVOLVE_ONE_SAMPLE // 3 - CONVOLVE_ONE_SAMPLE // 4 - CONVOLVE_ONE_SAMPLE // 5 - CONVOLVE_ONE_SAMPLE // 6 - CONVOLVE_ONE_SAMPLE // 7 - CONVOLVE_ONE_SAMPLE // 8 - CONVOLVE_ONE_SAMPLE // 9 - CONVOLVE_ONE_SAMPLE // 10 - - CONVOLVE_ONE_SAMPLE // 11 - CONVOLVE_ONE_SAMPLE // 12 - CONVOLVE_ONE_SAMPLE // 13 - CONVOLVE_ONE_SAMPLE // 14 - CONVOLVE_ONE_SAMPLE // 15 - CONVOLVE_ONE_SAMPLE // 16 - CONVOLVE_ONE_SAMPLE // 17 - CONVOLVE_ONE_SAMPLE // 18 - CONVOLVE_ONE_SAMPLE // 19 - CONVOLVE_ONE_SAMPLE // 20 - - CONVOLVE_ONE_SAMPLE // 21 - CONVOLVE_ONE_SAMPLE // 22 - CONVOLVE_ONE_SAMPLE // 23 - CONVOLVE_ONE_SAMPLE // 24 - CONVOLVE_ONE_SAMPLE // 25 - CONVOLVE_ONE_SAMPLE // 26 - CONVOLVE_ONE_SAMPLE // 27 - CONVOLVE_ONE_SAMPLE // 28 - CONVOLVE_ONE_SAMPLE // 29 - CONVOLVE_ONE_SAMPLE // 30 - - CONVOLVE_ONE_SAMPLE // 31 - CONVOLVE_ONE_SAMPLE // 32 - CONVOLVE_ONE_SAMPLE // 33 - CONVOLVE_ONE_SAMPLE // 34 - CONVOLVE_ONE_SAMPLE // 35 - CONVOLVE_ONE_SAMPLE // 36 - CONVOLVE_ONE_SAMPLE // 37 - CONVOLVE_ONE_SAMPLE // 38 - CONVOLVE_ONE_SAMPLE // 39 - CONVOLVE_ONE_SAMPLE // 40 - - CONVOLVE_ONE_SAMPLE // 41 - CONVOLVE_ONE_SAMPLE // 42 - CONVOLVE_ONE_SAMPLE // 43 - CONVOLVE_ONE_SAMPLE // 44 - CONVOLVE_ONE_SAMPLE // 45 - CONVOLVE_ONE_SAMPLE // 46 - CONVOLVE_ONE_SAMPLE // 47 - CONVOLVE_ONE_SAMPLE // 48 - CONVOLVE_ONE_SAMPLE // 49 - CONVOLVE_ONE_SAMPLE // 50 - - CONVOLVE_ONE_SAMPLE // 51 - CONVOLVE_ONE_SAMPLE // 52 - CONVOLVE_ONE_SAMPLE // 53 - CONVOLVE_ONE_SAMPLE // 54 - CONVOLVE_ONE_SAMPLE // 55 - CONVOLVE_ONE_SAMPLE // 56 - CONVOLVE_ONE_SAMPLE // 57 - CONVOLVE_ONE_SAMPLE // 58 - CONVOLVE_ONE_SAMPLE // 59 - CONVOLVE_ONE_SAMPLE // 60 - - CONVOLVE_ONE_SAMPLE // 61 - CONVOLVE_ONE_SAMPLE // 62 - CONVOLVE_ONE_SAMPLE // 63 - CONVOLVE_ONE_SAMPLE // 64 - CONVOLVE_ONE_SAMPLE // 65 - CONVOLVE_ONE_SAMPLE // 66 - CONVOLVE_ONE_SAMPLE // 67 - CONVOLVE_ONE_SAMPLE // 68 - CONVOLVE_ONE_SAMPLE // 69 - CONVOLVE_ONE_SAMPLE // 70 - - CONVOLVE_ONE_SAMPLE // 71 - CONVOLVE_ONE_SAMPLE // 72 - CONVOLVE_ONE_SAMPLE // 73 - CONVOLVE_ONE_SAMPLE // 74 - CONVOLVE_ONE_SAMPLE // 75 - CONVOLVE_ONE_SAMPLE // 76 - CONVOLVE_ONE_SAMPLE // 77 - CONVOLVE_ONE_SAMPLE // 78 - CONVOLVE_ONE_SAMPLE // 79 - CONVOLVE_ONE_SAMPLE // 80 - - CONVOLVE_ONE_SAMPLE // 81 - CONVOLVE_ONE_SAMPLE // 82 - CONVOLVE_ONE_SAMPLE // 83 - CONVOLVE_ONE_SAMPLE // 84 - CONVOLVE_ONE_SAMPLE // 85 - CONVOLVE_ONE_SAMPLE // 86 - CONVOLVE_ONE_SAMPLE // 87 - CONVOLVE_ONE_SAMPLE // 88 - CONVOLVE_ONE_SAMPLE // 89 - CONVOLVE_ONE_SAMPLE // 90 - - CONVOLVE_ONE_SAMPLE // 91 - CONVOLVE_ONE_SAMPLE // 92 - CONVOLVE_ONE_SAMPLE // 93 - CONVOLVE_ONE_SAMPLE // 94 - CONVOLVE_ONE_SAMPLE // 95 - CONVOLVE_ONE_SAMPLE // 96 - CONVOLVE_ONE_SAMPLE // 97 - CONVOLVE_ONE_SAMPLE // 98 - CONVOLVE_ONE_SAMPLE // 99 - CONVOLVE_ONE_SAMPLE // 100 - - CONVOLVE_ONE_SAMPLE // 101 - CONVOLVE_ONE_SAMPLE // 102 - CONVOLVE_ONE_SAMPLE // 103 - CONVOLVE_ONE_SAMPLE // 104 - CONVOLVE_ONE_SAMPLE // 105 - CONVOLVE_ONE_SAMPLE // 106 - CONVOLVE_ONE_SAMPLE // 107 - CONVOLVE_ONE_SAMPLE // 108 - CONVOLVE_ONE_SAMPLE // 109 - CONVOLVE_ONE_SAMPLE // 110 - - CONVOLVE_ONE_SAMPLE // 111 - CONVOLVE_ONE_SAMPLE // 112 - CONVOLVE_ONE_SAMPLE // 113 - CONVOLVE_ONE_SAMPLE // 114 - CONVOLVE_ONE_SAMPLE // 115 - CONVOLVE_ONE_SAMPLE // 116 - CONVOLVE_ONE_SAMPLE // 117 - CONVOLVE_ONE_SAMPLE // 118 - CONVOLVE_ONE_SAMPLE // 119 - CONVOLVE_ONE_SAMPLE // 120 - - CONVOLVE_ONE_SAMPLE // 121 - CONVOLVE_ONE_SAMPLE // 122 - CONVOLVE_ONE_SAMPLE // 123 - CONVOLVE_ONE_SAMPLE // 124 - CONVOLVE_ONE_SAMPLE // 125 - CONVOLVE_ONE_SAMPLE // 126 - CONVOLVE_ONE_SAMPLE // 127 - CONVOLVE_ONE_SAMPLE // 128 - } else { - while (j < kernelSize) { - // Non-optimized using actual while loop. - CONVOLVE_ONE_SAMPLE - } - } - destP[i++] = sum; - } - - // Copy 2nd half of input buffer to 1st half. - memcpy(m_buffer.Elements(), inputP, sizeof(float) * framesToProcess); -} - -void DirectConvolver::reset() -{ - PodZero(m_buffer.Elements(), m_buffer.Length()); -} - -} // namespace WebCore diff --git a/dom/media/webaudio/blink/DirectConvolver.h b/dom/media/webaudio/blink/DirectConvolver.h deleted file mode 100644 index 5732da6496..0000000000 --- a/dom/media/webaudio/blink/DirectConvolver.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2012 Intel 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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. - */ - -#ifndef DirectConvolver_h -#define DirectConvolver_h - -#include "nsTArray.h" -#include "mozilla/MemoryReporting.h" - -namespace WebCore { - -class DirectConvolver { -public: - explicit DirectConvolver(size_t inputBlockSize); - - void process(const nsTArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess); - - void reset(); - - size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const - { - size_t amount = aMallocSizeOf(this); - amount += m_buffer.ShallowSizeOfExcludingThis(aMallocSizeOf); - return amount; - } - - -private: - size_t m_inputBlockSize; - - nsTArray m_buffer; -}; - -} // namespace WebCore - -#endif // DirectConvolver_h diff --git a/dom/media/webaudio/blink/DynamicsCompressorKernel.cpp b/dom/media/webaudio/blink/DynamicsCompressorKernel.cpp index 4812c9a546..e5b4aba2f2 100644 --- a/dom/media/webaudio/blink/DynamicsCompressorKernel.cpp +++ b/dom/media/webaudio/blink/DynamicsCompressorKernel.cpp @@ -30,9 +30,9 @@ #include "DenormalDisabler.h" #include +#include #include "mozilla/FloatingPoint.h" -#include "mozilla/Constants.h" #include "WebAudioUtils.h" using namespace std; diff --git a/dom/media/webaudio/blink/FFTConvolver.cpp b/dom/media/webaudio/blink/FFTConvolver.cpp index 9d3305c7d1..8694073ae2 100644 --- a/dom/media/webaudio/blink/FFTConvolver.cpp +++ b/dom/media/webaudio/blink/FFTConvolver.cpp @@ -33,10 +33,11 @@ using namespace mozilla; namespace WebCore { -FFTConvolver::FFTConvolver(size_t fftSize) +FFTConvolver::FFTConvolver(size_t fftSize, size_t renderPhase) : m_frame(fftSize) - , m_readWriteIndex(0) + , m_readWriteIndex(renderPhase % (fftSize / 2)) { + MOZ_ASSERT(fftSize >= 2 * WEBAUDIO_BLOCK_SIZE); m_inputBuffer.SetLength(fftSize); PodZero(m_inputBuffer.Elements(), fftSize); m_outputBuffer.SetLength(fftSize); @@ -60,67 +61,47 @@ size_t FFTConvolver::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) co return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf); } -void FFTConvolver::process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess) +const float* FFTConvolver::process(FFTBlock* fftKernel, const float* sourceP) { size_t halfSize = fftSize() / 2; - // framesToProcess must be an exact multiple of halfSize, - // or halfSize is a multiple of framesToProcess when halfSize > framesToProcess. - bool isGood = !(halfSize % framesToProcess && framesToProcess % halfSize); - MOZ_ASSERT(isGood); - if (!isGood) - return; + // WEBAUDIO_BLOCK_SIZE must be an exact multiple of halfSize, + // halfSize must be a multiple of WEBAUDIO_BLOCK_SIZE + // and > WEBAUDIO_BLOCK_SIZE. + MOZ_ASSERT(halfSize % WEBAUDIO_BLOCK_SIZE == 0 && + WEBAUDIO_BLOCK_SIZE <= halfSize); - size_t numberOfDivisions = halfSize <= framesToProcess ? (framesToProcess / halfSize) : 1; - size_t divisionSize = numberOfDivisions == 1 ? framesToProcess : halfSize; + // Copy samples to input buffer (note contraint above!) + float* inputP = m_inputBuffer.Elements(); - for (size_t i = 0; i < numberOfDivisions; ++i, sourceP += divisionSize, destP += divisionSize) { - // Copy samples to input buffer (note contraint above!) - float* inputP = m_inputBuffer.Elements(); + MOZ_ASSERT(sourceP && inputP && m_readWriteIndex + WEBAUDIO_BLOCK_SIZE <= m_inputBuffer.Length()); - // Sanity check - bool isCopyGood1 = sourceP && inputP && m_readWriteIndex + divisionSize <= m_inputBuffer.Length(); - MOZ_ASSERT(isCopyGood1); - if (!isCopyGood1) - return; + memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * WEBAUDIO_BLOCK_SIZE); - memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * divisionSize); + float* outputP = m_outputBuffer.Elements(); + m_readWriteIndex += WEBAUDIO_BLOCK_SIZE; - // Copy samples from output buffer - float* outputP = m_outputBuffer.Elements(); + // Check if it's time to perform the next FFT + if (m_readWriteIndex == halfSize) { + // The input buffer is now filled (get frequency-domain version) + m_frame.PerformFFT(m_inputBuffer.Elements()); + m_frame.Multiply(*fftKernel); + m_frame.GetInverseWithoutScaling(m_outputBuffer.Elements()); - // Sanity check - bool isCopyGood2 = destP && outputP && m_readWriteIndex + divisionSize <= m_outputBuffer.Length(); - MOZ_ASSERT(isCopyGood2); - if (!isCopyGood2) - return; + // Overlap-add 1st half from previous time + AudioBufferAddWithScale(m_lastOverlapBuffer.Elements(), 1.0f, + m_outputBuffer.Elements(), halfSize); - memcpy(destP, outputP + m_readWriteIndex, sizeof(float) * divisionSize); - m_readWriteIndex += divisionSize; + // Finally, save 2nd half of result + MOZ_ASSERT(m_outputBuffer.Length() == 2 * halfSize && m_lastOverlapBuffer.Length() == halfSize); - // Check if it's time to perform the next FFT - if (m_readWriteIndex == halfSize) { - // The input buffer is now filled (get frequency-domain version) - m_frame.PerformFFT(m_inputBuffer.Elements()); - m_frame.Multiply(*fftKernel); - m_frame.GetInverseWithoutScaling(m_outputBuffer.Elements()); + memcpy(m_lastOverlapBuffer.Elements(), m_outputBuffer.Elements() + halfSize, sizeof(float) * halfSize); - // Overlap-add 1st half from previous time - AudioBufferAddWithScale(m_lastOverlapBuffer.Elements(), 1.0f, - m_outputBuffer.Elements(), halfSize); - - // Finally, save 2nd half of result - bool isCopyGood3 = m_outputBuffer.Length() == 2 * halfSize && m_lastOverlapBuffer.Length() == halfSize; - MOZ_ASSERT(isCopyGood3); - if (!isCopyGood3) - return; - - memcpy(m_lastOverlapBuffer.Elements(), m_outputBuffer.Elements() + halfSize, sizeof(float) * halfSize); - - // Reset index back to start for next time - m_readWriteIndex = 0; - } + // Reset index back to start for next time + m_readWriteIndex = 0; } + + return outputP + m_readWriteIndex; } void FFTConvolver::reset() @@ -129,4 +110,10 @@ void FFTConvolver::reset() m_readWriteIndex = 0; } +size_t FFTConvolver::latencyFrames() const +{ + return std::max(fftSize()/2, WEBAUDIO_BLOCK_SIZE) - + WEBAUDIO_BLOCK_SIZE; +} + } // namespace WebCore diff --git a/dom/media/webaudio/blink/FFTConvolver.h b/dom/media/webaudio/blink/FFTConvolver.h index 53cebf8c87..118c6baef5 100644 --- a/dom/media/webaudio/blink/FFTConvolver.h +++ b/dom/media/webaudio/blink/FFTConvolver.h @@ -40,24 +40,29 @@ using mozilla::FFTBlock; class FFTConvolver { public: - // fftSize must be a power of two - explicit FFTConvolver(size_t fftSize); + // |fftSize| must be a power of two. + // + // |renderPhase| is the initial offset in the initially zero input buffer. + // It is coordinated with the other stages, so they don't all do their + // FFTs at the same time. + explicit FFTConvolver(size_t fftSize, size_t renderPhase = 0); + // Process WEBAUDIO_BLOCK_SIZE elements of array |sourceP| and return a + // pointer to an output array of the same size. + // // |fftKernel| must be pre-scaled for FFTBlock::GetInverseWithoutScaling(). // - // For now, with multiple calls to Process(), framesToProcess MUST add up EXACTLY to fftSize / 2 - // // FIXME: Later, we can do more sophisticated buffering to relax this requirement... - // - // The input to output latency is equal to fftSize / 2 - // - // Processing in-place is allowed... - void process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess); + const float* process(FFTBlock* fftKernel, const float* sourceP); void reset(); size_t fftSize() const { return m_frame.FFTSize(); } + // The input to output latency is up to fftSize / 2, but alignment of the + // FFTs with the blocks reduces this by one block. + size_t latencyFrames() const; + size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; diff --git a/dom/media/webaudio/blink/HRTFElevation.cpp b/dom/media/webaudio/blink/HRTFElevation.cpp index f53943d6c5..c6e0430885 100644 --- a/dom/media/webaudio/blink/HRTFElevation.cpp +++ b/dom/media/webaudio/blink/HRTFElevation.cpp @@ -76,19 +76,19 @@ size_t HRTFElevation::fftSizeForSampleRate(float sampleRate) // This is the size if we were to use all raw response samples. unsigned resampledLength = floorf(ResponseFrameSize * sampleRate / rawSampleRate); - // Keep things semi-sane, with max FFT size of 1024 and minimum of 4. - // "size |= 3" ensures a minimum of 4 (with the size++ below) and sets the - // 2 least significant bits for rounding up to the next power of 2 below. + // Keep things semi-sane, with max FFT size of 1024. unsigned size = min(resampledLength, 1023U); - size |= 3; + // Ensure a minimum of 2 * WEBAUDIO_BLOCK_SIZE (with the size++ below) for + // FFTConvolver and set the 8 least significant bits for rounding up to + // the next power of 2 below. + size |= 2 * WEBAUDIO_BLOCK_SIZE - 1; // Round up to the next power of 2, making the FFT size no more than twice // the impulse response length. This doubles size for values that are - // already powers of 2. This works by filling in 7 bits to right of the + // already powers of 2. This works by filling in alls bit to right of the // most significant bit. The most significant bit is no greater than - // 1 << 9, and the least significant 2 bits were already set above. + // 1 << 9, and the least significant 8 bits were already set above, so + // there is at most one bit to add. size |= (size >> 1); - size |= (size >> 2); - size |= (size >> 4); size++; MOZ_ASSERT((size & (size - 1)) == 0); diff --git a/dom/media/webaudio/blink/HRTFPanner.cpp b/dom/media/webaudio/blink/HRTFPanner.cpp index 11c2728941..c97ce47670 100644 --- a/dom/media/webaudio/blink/HRTFPanner.cpp +++ b/dom/media/webaudio/blink/HRTFPanner.cpp @@ -59,11 +59,6 @@ HRTFPanner::HRTFPanner(float sampleRate, already_AddRefed da { MOZ_ASSERT(m_databaseLoader); MOZ_COUNT_CTOR(HRTFPanner); - - m_tempL1.SetLength(RenderingQuantum); - m_tempR1.SetLength(RenderingQuantum); - m_tempL2.SetLength(RenderingQuantum); - m_tempR2.SetLength(RenderingQuantum); } HRTFPanner::~HRTFPanner() @@ -81,10 +76,6 @@ size_t HRTFPanner::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) cons amount += m_convolverL2.sizeOfExcludingThis(aMallocSizeOf); amount += m_convolverR2.sizeOfExcludingThis(aMallocSizeOf); amount += m_delayLine.SizeOfExcludingThis(aMallocSizeOf); - amount += m_tempL1.ShallowSizeOfExcludingThis(aMallocSizeOf); - amount += m_tempL2.ShallowSizeOfExcludingThis(aMallocSizeOf); - amount += m_tempR1.ShallowSizeOfExcludingThis(aMallocSizeOf); - amount += m_tempR2.ShallowSizeOfExcludingThis(aMallocSizeOf); return amount; } @@ -256,23 +247,26 @@ void HRTFPanner::pan(double desiredAzimuth, double elevation, const AudioBlock* bool needsCrossfading = m_crossfadeIncr; - // Have the convolvers render directly to the final destination if we're not cross-fading. - float* convolutionDestinationL1 = needsCrossfading ? m_tempL1.Elements() : destinationL; - float* convolutionDestinationR1 = needsCrossfading ? m_tempR1.Elements() : destinationR; - float* convolutionDestinationL2 = needsCrossfading ? m_tempL2.Elements() : destinationL; - float* convolutionDestinationR2 = needsCrossfading ? m_tempR2.Elements() : destinationR; + const float* convolutionDestinationL1; + const float* convolutionDestinationR1; + const float* convolutionDestinationL2; + const float* convolutionDestinationR2; // Now do the convolutions. // Note that we avoid doing convolutions on both sets of convolvers if we're not currently cross-fading. if (m_crossfadeSelection == CrossfadeSelection1 || needsCrossfading) { - m_convolverL1.process(kernelL1->fftFrame(), destinationL, convolutionDestinationL1, WEBAUDIO_BLOCK_SIZE); - m_convolverR1.process(kernelR1->fftFrame(), destinationR, convolutionDestinationR1, WEBAUDIO_BLOCK_SIZE); + convolutionDestinationL1 = + m_convolverL1.process(kernelL1->fftFrame(), destinationL); + convolutionDestinationR1 = + m_convolverR1.process(kernelR1->fftFrame(), destinationR); } if (m_crossfadeSelection == CrossfadeSelection2 || needsCrossfading) { - m_convolverL2.process(kernelL2->fftFrame(), destinationL, convolutionDestinationL2, WEBAUDIO_BLOCK_SIZE); - m_convolverR2.process(kernelR2->fftFrame(), destinationR, convolutionDestinationR2, WEBAUDIO_BLOCK_SIZE); + convolutionDestinationL2 = + m_convolverL2.process(kernelL2->fftFrame(), destinationL); + convolutionDestinationR2 = + m_convolverR2.process(kernelR2->fftFrame(), destinationR); } if (needsCrossfading) { @@ -298,6 +292,18 @@ void HRTFPanner::pan(double desiredAzimuth, double elevation, const AudioBlock* m_crossfadeX = 0; m_crossfadeIncr = 0; } + } else { + const float* sourceL; + const float* sourceR; + if (m_crossfadeSelection == CrossfadeSelection1) { + sourceL = convolutionDestinationL1; + sourceR = convolutionDestinationR1; + } else { + sourceL = convolutionDestinationL2; + sourceR = convolutionDestinationR2; + } + PodCopy(destinationL, sourceL, WEBAUDIO_BLOCK_SIZE); + PodCopy(destinationR, sourceR, WEBAUDIO_BLOCK_SIZE); } } @@ -307,10 +313,12 @@ int HRTFPanner::maxTailFrames() const // response, there is additional tail time from the approximations in the // implementation. Because HRTFPanner is implemented with a DelayKernel // and a FFTConvolver, the tailTime of the HRTFPanner is the sum of the - // tailTime of the DelayKernel and the tailTime of the FFTConvolver. - // The FFTConvolver has a tail time of fftSize(), including latency of - // fftSize()/2. - return m_delayLine.MaxDelayTicks() + fftSize(); + // tailTime of the DelayKernel and the tailTime of the FFTConvolver. The + // FFTs of the convolver are fftSize(), half of which is latency, but this + // is aligned with blocks and so is reduced by the one block which is + // processed immediately. + return m_delayLine.MaxDelayTicks() + + m_convolverL1.fftSize()/2 + m_convolverL1.latencyFrames(); } } // namespace WebCore diff --git a/dom/media/webaudio/blink/PeriodicWave.cpp b/dom/media/webaudio/blink/PeriodicWave.cpp index d587a27c15..763c52fecb 100644 --- a/dom/media/webaudio/blink/PeriodicWave.cpp +++ b/dom/media/webaudio/blink/PeriodicWave.cpp @@ -29,10 +29,11 @@ #include "PeriodicWave.h" #include #include +#include #include "mozilla/FFTBlock.h" -const unsigned PeriodicWaveSize = 4096; // This must be a power of two. -const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges. +const unsigned MinPeriodicWaveSize = 4096; // This must be a power of two. +const unsigned MaxPeriodicWaveSize = 8192; // This must be a power of two. const float CentsPerRange = 1200 / 3; // 1/3 Octave. using namespace mozilla; @@ -40,57 +41,87 @@ using mozilla::dom::OscillatorType; namespace WebCore { -PeriodicWave* PeriodicWave::create(float sampleRate, - const float* real, - const float* imag, - size_t numberOfComponents) +already_AddRefed +PeriodicWave::create(float sampleRate, + const float* real, + const float* imag, + size_t numberOfComponents) { - bool isGood = real && imag && numberOfComponents > 0 && - numberOfComponents <= PeriodicWaveSize; + bool isGood = real && imag && numberOfComponents > 0; MOZ_ASSERT(isGood); if (isGood) { - PeriodicWave* periodicWave = new PeriodicWave(sampleRate); - periodicWave->createBandLimitedTables(real, imag, numberOfComponents); - return periodicWave; + RefPtr periodicWave = + new PeriodicWave(sampleRate, numberOfComponents); + + // Limit the number of components used to those for frequencies below the + // Nyquist of the fixed length inverse FFT. + size_t halfSize = periodicWave->m_periodicWaveSize / 2; + numberOfComponents = std::min(numberOfComponents, halfSize); + periodicWave->m_numberOfComponents = numberOfComponents; + periodicWave->m_realComponents = new AudioFloatArray(numberOfComponents); + periodicWave->m_imagComponents = new AudioFloatArray(numberOfComponents); + memcpy(periodicWave->m_realComponents->Elements(), real, + numberOfComponents * sizeof(float)); + memcpy(periodicWave->m_imagComponents->Elements(), imag, + numberOfComponents * sizeof(float)); + + return periodicWave.forget(); } - return 0; + return nullptr; } -PeriodicWave* PeriodicWave::createSine(float sampleRate) +already_AddRefed +PeriodicWave::createSine(float sampleRate) { - PeriodicWave* periodicWave = new PeriodicWave(sampleRate); - periodicWave->generateBasicWaveform(OscillatorType::Sine); - return periodicWave; + RefPtr periodicWave = + new PeriodicWave(sampleRate, MinPeriodicWaveSize); + periodicWave->generateBasicWaveform(OscillatorType::Sine); + return periodicWave.forget(); } -PeriodicWave* PeriodicWave::createSquare(float sampleRate) +already_AddRefed +PeriodicWave::createSquare(float sampleRate) { - PeriodicWave* periodicWave = new PeriodicWave(sampleRate); - periodicWave->generateBasicWaveform(OscillatorType::Square); - return periodicWave; + RefPtr periodicWave = + new PeriodicWave(sampleRate, MinPeriodicWaveSize); + periodicWave->generateBasicWaveform(OscillatorType::Square); + return periodicWave.forget(); } -PeriodicWave* PeriodicWave::createSawtooth(float sampleRate) +already_AddRefed +PeriodicWave::createSawtooth(float sampleRate) { - PeriodicWave* periodicWave = new PeriodicWave(sampleRate); - periodicWave->generateBasicWaveform(OscillatorType::Sawtooth); - return periodicWave; + RefPtr periodicWave = + new PeriodicWave(sampleRate, MinPeriodicWaveSize); + periodicWave->generateBasicWaveform(OscillatorType::Sawtooth); + return periodicWave.forget(); } -PeriodicWave* PeriodicWave::createTriangle(float sampleRate) +already_AddRefed +PeriodicWave::createTriangle(float sampleRate) { - PeriodicWave* periodicWave = new PeriodicWave(sampleRate); - periodicWave->generateBasicWaveform(OscillatorType::Triangle); - return periodicWave; + RefPtr periodicWave = + new PeriodicWave(sampleRate, MinPeriodicWaveSize); + periodicWave->generateBasicWaveform(OscillatorType::Triangle); + return periodicWave.forget(); } -PeriodicWave::PeriodicWave(float sampleRate) +PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents) : m_sampleRate(sampleRate) - , m_periodicWaveSize(PeriodicWaveSize) - , m_numberOfRanges(NumberOfRanges) , m_centsPerRange(CentsPerRange) + , m_lowestRequestedFundamentalFrequency(std::numeric_limits::max()) { float nyquist = 0.5 * m_sampleRate; + + if (numberOfComponents <= MinPeriodicWaveSize) { + m_periodicWaveSize = MinPeriodicWaveSize; + } else { + unsigned npow2 = powf(2.0f, floorf(logf(numberOfComponents - 1.0)/logf(2.0f) + 1.0f)); + m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2); + } + + m_numberOfRanges = (unsigned)(3.0f*logf(m_periodicWaveSize)/logf(2.0f)); + m_bandLimitedTables.SetLength(m_numberOfRanges); m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials(); m_rateScale = m_periodicWaveSize / m_sampleRate; } @@ -111,10 +142,22 @@ size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) co void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor) { + // Negative frequencies are allowed, in which case we alias // to the positive frequency. fundamentalFrequency = fabsf(fundamentalFrequency); + if (fundamentalFrequency < m_lowestRequestedFundamentalFrequency) { + for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { + m_bandLimitedTables[rangeIndex] = 0; + } + + // We need to create the first table to determine the normalization + // constant. + createBandLimitedTables(fundamentalFrequency, 0); + m_lowestRequestedFundamentalFrequency = fundamentalFrequency; + } + // Calculate the pitch range. float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5; float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200; @@ -133,11 +176,17 @@ void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, f unsigned rangeIndex1 = static_cast(pitchRange); unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1; + if (!m_bandLimitedTables[rangeIndex1].get()) + createBandLimitedTables(fundamentalFrequency, rangeIndex1); + + if (!m_bandLimitedTables[rangeIndex2].get()) + createBandLimitedTables(fundamentalFrequency, rangeIndex2); + lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements(); higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements(); // Ranges from 0 -> 1 to interpolate between lower -> higher. - tableInterpolationFactor = pitchRange - rangeIndex1; + tableInterpolationFactor = rangeIndex2 - pitchRange; } unsigned PeriodicWave::maxNumberOfPartials() const @@ -163,101 +212,81 @@ unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const // One table is created for each range for non-aliasing playback // at different playback rates. Thus, higher ranges have more // high-frequency partials culled out. -void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents) +void PeriodicWave::createBandLimitedTables(float fundamentalFrequency, + unsigned rangeIndex) { - float normalizationScale = 1; - unsigned fftSize = m_periodicWaveSize; - unsigned halfSize = fftSize / 2 + 1; unsigned i; - numberOfComponents = std::min(numberOfComponents, halfSize); + const float *realData = m_realComponents->Elements(); + const float *imagData = m_imagComponents->Elements(); - m_bandLimitedTables.SetCapacity(m_numberOfRanges); + // This FFTBlock is used to cull partials (represented by frequency bins). + FFTBlock frame(fftSize); - for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { - // This FFTBlock is used to cull partials (represented by frequency bins). - FFTBlock frame(fftSize); - nsAutoArrayPtr realP(new float[halfSize]); - nsAutoArrayPtr imagP(new float[halfSize]); + // Find the starting bin where we should start culling the aliasing + // partials for this pitch range. We need to clear out the highest + // frequencies to band-limit the waveform. + unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); + // Also limit to the number of components that are provided. + numberOfPartials = std::min(numberOfPartials, m_numberOfComponents - 1); - // Copy from loaded frequency data and scale. - float scale = fftSize; - AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents); - AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents); + // Limit number of partials to those below Nyquist frequency + float nyquist = 0.5 * m_sampleRate; + numberOfPartials = std::min(numberOfPartials, + (unsigned)(nyquist / fundamentalFrequency)); - // If fewer components were provided than 1/2 FFT size, - // then clear the remaining bins. - for (i = numberOfComponents; i < halfSize; ++i) { - realP[i] = 0; - imagP[i] = 0; - } - - // Generate complex conjugate because of the way the - // inverse FFT is defined. - float minusOne = -1; - AudioBufferInPlaceScale(imagP, minusOne, halfSize); - - // Find the starting bin where we should start culling. - // We need to clear out the highest frequencies to band-limit - // the waveform. - unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); - - // Cull the aliasing partials for this pitch range. - for (i = numberOfPartials + 1; i < halfSize; ++i) { - realP[i] = 0; - imagP[i] = 0; - } - // Clear nyquist if necessary. - if (numberOfPartials < halfSize) - realP[halfSize-1] = 0; - - // Clear any DC-offset. - realP[0] = 0; - - // Clear values which have no effect. - imagP[0] = 0; - imagP[halfSize-1] = 0; - - // Create the band-limited table. - AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize); - m_bandLimitedTables.AppendElement(table); - - // Apply an inverse FFT to generate the time-domain table data. - float* data = m_bandLimitedTables[rangeIndex]->Elements(); - frame.PerformInverseFFT(realP, imagP, data); - - // For the first range (which has the highest power), calculate - // its peak value then compute normalization scale. - if (!rangeIndex) { - float maxValue; - maxValue = AudioBufferPeakValue(data, m_periodicWaveSize); - - if (maxValue) - normalizationScale = 1.0f / maxValue; - } - - // Apply normalization scale. - AudioBufferInPlaceScale(data, normalizationScale, m_periodicWaveSize); + // Copy from loaded frequency data and generate complex conjugate + // because of the way the inverse FFT is defined. + // The coefficients of higher partials remain zero, as initialized in + // the FFTBlock constructor. + for (i = 0; i < numberOfPartials + 1; ++i) { + frame.RealData(i) = realData[i]; + frame.ImagData(i) = -imagData[i]; } + + // Clear any DC-offset. + frame.RealData(0) = 0; + // Clear value which has no effect. + frame.ImagData(0) = 0; + + // Create the band-limited table. + AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize); + m_bandLimitedTables[rangeIndex] = table; + + // Apply an inverse FFT to generate the time-domain table data. + float* data = m_bandLimitedTables[rangeIndex]->Elements(); + frame.GetInverseWithoutScaling(data); + + // For the first range (which has the highest power), calculate + // its peak value then compute normalization scale. + if (!rangeIndex) { + float maxValue; + maxValue = AudioBufferPeakValue(data, m_periodicWaveSize); + + if (maxValue) + m_normalizationScale = 1.0f / maxValue; + } + + // Apply normalization scale. + AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize); } void PeriodicWave::generateBasicWaveform(OscillatorType shape) { const float piFloat = float(M_PI); unsigned fftSize = periodicWaveSize(); - unsigned halfSize = fftSize / 2 + 1; + unsigned halfSize = fftSize / 2; - AudioFloatArray real(halfSize); - AudioFloatArray imag(halfSize); - float* realP = real.Elements(); - float* imagP = imag.Elements(); + m_numberOfComponents = halfSize; + m_realComponents = new AudioFloatArray(halfSize); + m_imagComponents = new AudioFloatArray(halfSize); + float* realP = m_realComponents->Elements(); + float* imagP = m_imagComponents->Elements(); - // Clear DC and Nyquist. + // Clear DC and imag value which is ignored. realP[0] = 0; imagP[0] = 0; - realP[halfSize-1] = 0; - imagP[halfSize-1] = 0; for (unsigned n = 1; n < halfSize; ++n) { float omega = 2 * piFloat * n; @@ -308,8 +337,6 @@ void PeriodicWave::generateBasicWaveform(OscillatorType shape) realP[n] = a; imagP[n] = b; } - - createBandLimitedTables(realP, imagP, halfSize); } } // namespace WebCore diff --git a/dom/media/webaudio/blink/PeriodicWave.h b/dom/media/webaudio/blink/PeriodicWave.h index 6983e8cb15..a0b6cdd111 100644 --- a/dom/media/webaudio/blink/PeriodicWave.h +++ b/dom/media/webaudio/blink/PeriodicWave.h @@ -42,20 +42,19 @@ typedef nsTArray AudioFloatArray; class PeriodicWave { public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebCore::PeriodicWave); - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PeriodicWave); - - static PeriodicWave* createSine(float sampleRate); - static PeriodicWave* createSquare(float sampleRate); - static PeriodicWave* createSawtooth(float sampleRate); - static PeriodicWave* createTriangle(float sampleRate); + static already_AddRefed createSine(float sampleRate); + static already_AddRefed createSquare(float sampleRate); + static already_AddRefed createSawtooth(float sampleRate); + static already_AddRefed createTriangle(float sampleRate); // Creates an arbitrary periodic wave given the frequency components // (Fourier coefficients). - static PeriodicWave* create(float sampleRate, - const float* real, - const float* imag, - size_t numberOfComponents); + static already_AddRefed create(float sampleRate, + const float* real, + const float* imag, + size_t numberOfComponents); // Returns pointers to the lower and higher wave data for the pitch range // containing the given fundamental frequency. These two tables are in @@ -77,7 +76,7 @@ public: size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; private: - explicit PeriodicWave(float sampleRate); + explicit PeriodicWave(float sampleRate, size_t numberOfComponents); ~PeriodicWave() {} void generateBasicWaveform(mozilla::dom::OscillatorType); @@ -86,6 +85,9 @@ private: unsigned m_periodicWaveSize; unsigned m_numberOfRanges; float m_centsPerRange; + unsigned m_numberOfComponents; + nsAutoPtr m_realComponents; + nsAutoPtr m_imagComponents; // The lowest frequency (in Hertz) where playback will include all of the // partials. Playing back lower than this frequency will gradually lose @@ -102,8 +104,10 @@ private: unsigned numberOfPartialsForRange(unsigned rangeIndex) const; - // Creates tables based on numberOfComponents Fourier coefficients. - void createBandLimitedTables(const float* real, const float* imag, unsigned numberOfComponents); + // Creates table for specified index based on fundamental frequency. + void createBandLimitedTables(float fundamentalFrequency, unsigned rangeIndex); + float m_lowestRequestedFundamentalFrequency; + float m_normalizationScale; nsTArray > m_bandLimitedTables; }; diff --git a/dom/media/webaudio/blink/Reverb.cpp b/dom/media/webaudio/blink/Reverb.cpp index a54f92978b..39f9d59c93 100644 --- a/dom/media/webaudio/blink/Reverb.cpp +++ b/dom/media/webaudio/blink/Reverb.cpp @@ -77,7 +77,7 @@ static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* respo return scale; } -Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate) +Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate) { float scale = 1; @@ -101,7 +101,7 @@ Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulse } } - initialize(irChannels, impulseResponseBufferLength, renderSliceSize, + initialize(irChannels, impulseResponseBufferLength, maxFFTSize, numberOfChannels, useBackgroundThreads); } @@ -121,7 +121,7 @@ size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const void Reverb::initialize(const nsTArray& impulseResponseBuffer, - size_t impulseResponseBufferLength, size_t renderSliceSize, + size_t impulseResponseBufferLength, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads) { m_impulseResponseLength = impulseResponseBufferLength; @@ -135,10 +135,10 @@ void Reverb::initialize(const nsTArray& impulseResponseBuffer, const float* channel = impulseResponseBuffer[i]; size_t length = impulseResponseBufferLength; - nsAutoPtr convolver(new ReverbConvolver(channel, length, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads)); + nsAutoPtr convolver(new ReverbConvolver(channel, length, maxFFTSize, convolverRenderPhase, useBackgroundThreads)); m_convolvers.AppendElement(convolver.forget()); - convolverRenderPhase += renderSliceSize; + convolverRenderPhase += WEBAUDIO_BLOCK_SIZE; } // For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method. @@ -149,12 +149,12 @@ void Reverb::initialize(const nsTArray& impulseResponseBuffer, } } -void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, size_t framesToProcess) +void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus) { // Do a fairly comprehensive sanity check. // If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases. bool isSafeToProcess = sourceBus && destinationBus && sourceBus->ChannelCount() > 0 && destinationBus->mChannelData.Length() > 0 - && framesToProcess <= MaxFrameSize && framesToProcess <= size_t(sourceBus->GetDuration()) && framesToProcess <= size_t(destinationBus->GetDuration()); + && WEBAUDIO_BLOCK_SIZE <= MaxFrameSize && WEBAUDIO_BLOCK_SIZE <= size_t(sourceBus->GetDuration()) && WEBAUDIO_BLOCK_SIZE <= size_t(destinationBus->GetDuration()); MOZ_ASSERT(isSafeToProcess); if (!isSafeToProcess) @@ -175,28 +175,28 @@ void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, si // 2 -> 2 -> 2 const float* sourceBusR = static_cast(sourceBus->mChannelData[1]); float* destinationChannelR = static_cast(const_cast(destinationBus->mChannelData[1])); - m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess); - m_convolvers[1]->process(sourceBusR, sourceBus->GetDuration(), destinationChannelR, destinationBus->GetDuration(), framesToProcess); + m_convolvers[0]->process(sourceBusL, destinationChannelL); + m_convolvers[1]->process(sourceBusR, destinationChannelR); } else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) { // 1 -> 2 -> 2 for (int i = 0; i < 2; ++i) { float* destinationChannel = static_cast(const_cast(destinationBus->mChannelData[i])); - m_convolvers[i]->process(sourceBusL, sourceBus->GetDuration(), destinationChannel, destinationBus->GetDuration(), framesToProcess); + m_convolvers[i]->process(sourceBusL, destinationChannel); } } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) { // 1 -> 1 -> 2 - m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess); + m_convolvers[0]->process(sourceBusL, destinationChannelL); // simply copy L -> R float* destinationChannelR = static_cast(const_cast(destinationBus->mChannelData[1])); - bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->GetDuration()) >= framesToProcess; + bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->GetDuration()) >= WEBAUDIO_BLOCK_SIZE; MOZ_ASSERT(isCopySafe); if (!isCopySafe) return; - PodCopy(destinationChannelR, destinationChannelL, framesToProcess); + PodCopy(destinationChannelR, destinationChannelL, WEBAUDIO_BLOCK_SIZE); } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) { // 1 -> 1 -> 1 - m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess); + m_convolvers[0]->process(sourceBusL, destinationChannelL); } else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) { // 2 -> 4 -> 2 ("True" stereo) const float* sourceBusR = static_cast(sourceBus->mChannelData[1]); @@ -206,12 +206,12 @@ void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, si float* tempChannelR = static_cast(const_cast(m_tempBuffer.mChannelData[1])); // Process left virtual source - m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess); - m_convolvers[1]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelR, destinationBus->GetDuration(), framesToProcess); + m_convolvers[0]->process(sourceBusL, destinationChannelL); + m_convolvers[1]->process(sourceBusL, destinationChannelR); // Process right virtual source - m_convolvers[2]->process(sourceBusR, sourceBus->GetDuration(), tempChannelL, m_tempBuffer.GetDuration(), framesToProcess); - m_convolvers[3]->process(sourceBusR, sourceBus->GetDuration(), tempChannelR, m_tempBuffer.GetDuration(), framesToProcess); + m_convolvers[2]->process(sourceBusR, tempChannelL); + m_convolvers[3]->process(sourceBusR, tempChannelR); AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->GetDuration()); AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->GetDuration()); @@ -224,12 +224,12 @@ void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, si float* tempChannelR = static_cast(const_cast(m_tempBuffer.mChannelData[1])); // Process left virtual source - m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess); - m_convolvers[1]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelR, destinationBus->GetDuration(), framesToProcess); + m_convolvers[0]->process(sourceBusL, destinationChannelL); + m_convolvers[1]->process(sourceBusL, destinationChannelR); // Process right virtual source - m_convolvers[2]->process(sourceBusL, sourceBus->GetDuration(), tempChannelL, m_tempBuffer.GetDuration(), framesToProcess); - m_convolvers[3]->process(sourceBusL, sourceBus->GetDuration(), tempChannelR, m_tempBuffer.GetDuration(), framesToProcess); + m_convolvers[2]->process(sourceBusL, tempChannelL); + m_convolvers[3]->process(sourceBusL, tempChannelR); AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->GetDuration()); AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->GetDuration()); @@ -240,15 +240,4 @@ void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, si } } -void Reverb::reset() -{ - for (size_t i = 0; i < m_convolvers.Length(); ++i) - m_convolvers[i]->reset(); -} - -size_t Reverb::latencyFrames() const -{ - return !m_convolvers.IsEmpty() ? m_convolvers[0]->latencyFrames() : 0; -} - } // namespace WebCore diff --git a/dom/media/webaudio/blink/Reverb.h b/dom/media/webaudio/blink/Reverb.h index fd4445ec33..35e72283dc 100644 --- a/dom/media/webaudio/blink/Reverb.h +++ b/dom/media/webaudio/blink/Reverb.h @@ -48,18 +48,22 @@ public: enum { MaxFrameSize = 256 }; // renderSliceSize is a rendering hint, so the FFTs can be optimized to not all occur at the same time (very bad when rendering on a real-time thread). - Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate); + Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, + size_t impulseResponseBufferLength, size_t maxFFTSize, + size_t numberOfChannels, bool useBackgroundThreads, bool normalize, + float sampleRate); - void process(const mozilla::AudioBlock* sourceBus, mozilla::AudioBlock* destinationBus, size_t framesToProcess); - void reset(); + void process(const mozilla::AudioBlock* sourceBus, + mozilla::AudioBlock* destinationBus); size_t impulseResponseLength() const { return m_impulseResponseLength; } - size_t latencyFrames() const; size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; private: - void initialize(const nsTArray& impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads); + void initialize(const nsTArray& impulseResponseBuffer, + size_t impulseResponseBufferLength, size_t maxFFTSize, + size_t numberOfChannels, bool useBackgroundThreads); size_t m_impulseResponseLength; diff --git a/dom/media/webaudio/blink/ReverbAccumulationBuffer.cpp b/dom/media/webaudio/blink/ReverbAccumulationBuffer.cpp index 6b92e9983e..18d2228944 100644 --- a/dom/media/webaudio/blink/ReverbAccumulationBuffer.cpp +++ b/dom/media/webaudio/blink/ReverbAccumulationBuffer.cpp @@ -76,7 +76,7 @@ void ReverbAccumulationBuffer::updateReadIndex(int* readIndex, size_t numberOfFr *readIndex = (*readIndex + numberOfFrames) % m_buffer.Length(); } -int ReverbAccumulationBuffer::accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames) +int ReverbAccumulationBuffer::accumulate(const float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames) { size_t bufferLength = m_buffer.Length(); diff --git a/dom/media/webaudio/blink/ReverbAccumulationBuffer.h b/dom/media/webaudio/blink/ReverbAccumulationBuffer.h index e07e71061c..97ee94d271 100644 --- a/dom/media/webaudio/blink/ReverbAccumulationBuffer.h +++ b/dom/media/webaudio/blink/ReverbAccumulationBuffer.h @@ -50,7 +50,7 @@ public: // We need to pass in and update readIndex here, since each ReverbConvolverStage may be running in // a different thread than the realtime thread calling ReadAndClear() and maintaining m_readIndex // Returns the writeIndex where the accumulation took place - int accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames); + int accumulate(const float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames); size_t readIndex() const { return m_readIndex; } void updateReadIndex(int* readIndex, size_t numberOfFrames) const; diff --git a/dom/media/webaudio/blink/ReverbConvolver.cpp b/dom/media/webaudio/blink/ReverbConvolver.cpp index 34212864d9..de707fe33a 100644 --- a/dom/media/webaudio/blink/ReverbConvolver.cpp +++ b/dom/media/webaudio/blink/ReverbConvolver.cpp @@ -49,29 +49,32 @@ const int InputBufferSize = 8 * 16384; // This was found to be a good value on Mac OS X, and may work well on other platforms as well, assuming // the very rough scheduling latencies are similar on these time-scales. Of course, this code may need to be // tuned for individual platforms if this assumption is found to be incorrect. -const size_t RealtimeFrameLimit = 8192 + 4096; // ~278msec @ 44.1KHz +const size_t RealtimeFrameLimit = 8192 + 4096 // ~278msec @ 44.1KHz + - WEBAUDIO_BLOCK_SIZE; +// First stage will have size MinFFTSize - successive stages will double in +// size each time until we hit the maximum size. +const size_t MinFFTSize = 256; +// If we are using background threads then don't exceed this FFT size for the +// stages which run in the real-time thread. This avoids having only one or +// two large stages (size 16384 or so) at the end which take a lot of time +// every several processing slices. This way we amortize the cost over more +// processing slices. +const size_t MaxRealtimeFFTSize = 4096; -const size_t MinFFTSize = 128; -const size_t MaxRealtimeFFTSize = 2048; - -ReverbConvolver::ReverbConvolver(const float* impulseResponseData, size_t impulseResponseLength, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads) +ReverbConvolver::ReverbConvolver(const float* impulseResponseData, + size_t impulseResponseLength, + size_t maxFFTSize, + size_t convolverRenderPhase, + bool useBackgroundThreads) : m_impulseResponseLength(impulseResponseLength) - , m_accumulationBuffer(impulseResponseLength + renderSliceSize) + , m_accumulationBuffer(impulseResponseLength + WEBAUDIO_BLOCK_SIZE) , m_inputBuffer(InputBufferSize) - , m_minFFTSize(MinFFTSize) // First stage will have this size - successive stages will double in size each time - , m_maxFFTSize(maxFFTSize) // until we hit m_maxFFTSize , m_backgroundThread("ConvolverWorker") , m_backgroundThreadCondition(&m_backgroundThreadLock) , m_useBackgroundThreads(useBackgroundThreads) , m_wantsToExit(false) , m_moreInputBuffered(false) { - // If we are using background threads then don't exceed this FFT size for the - // stages which run in the real-time thread. This avoids having only one or two - // large stages (size 16384 or so) at the end which take a lot of time every several - // processing slices. This way we amortize the cost over more processing slices. - m_maxRealtimeFFTSize = MaxRealtimeFFTSize; - // For the moment, a good way to know if we have real-time constraint is to check if we're using background threads. // Otherwise, assume we're being run from a command-line tool. bool hasRealtimeConstraint = useBackgroundThreads; @@ -79,26 +82,35 @@ ReverbConvolver::ReverbConvolver(const float* impulseResponseData, size_t impuls const float* response = impulseResponseData; size_t totalResponseLength = impulseResponseLength; - // The total latency is zero because the direct-convolution is used in the leading portion. + // The total latency is zero because the first FFT stage is small enough + // to return output in the first block. size_t reverbTotalLatency = 0; size_t stageOffset = 0; - int i = 0; - size_t fftSize = m_minFFTSize; + size_t stagePhase = 0; + size_t fftSize = MinFFTSize; while (stageOffset < totalResponseLength) { size_t stageSize = fftSize / 2; // For the last stage, it's possible that stageOffset is such that we're straddling the end // of the impulse response buffer (if we use stageSize), so reduce the last stage's length... - if (stageSize + stageOffset > totalResponseLength) + if (stageSize + stageOffset > totalResponseLength) { stageSize = totalResponseLength - stageOffset; + // Use smallest FFT that is large enough to cover the last stage. + fftSize = MinFFTSize; + while (stageSize * 2 > fftSize) { + fftSize *= 2; + } + } // This "staggers" the time when each FFT happens so they don't all happen at the same time - int renderPhase = convolverRenderPhase + i * renderSliceSize; + int renderPhase = convolverRenderPhase + stagePhase; - bool useDirectConvolver = !stageOffset; - - nsAutoPtr stage(new ReverbConvolverStage(response, totalResponseLength, reverbTotalLatency, stageOffset, stageSize, fftSize, renderPhase, renderSliceSize, &m_accumulationBuffer, useDirectConvolver)); + nsAutoPtr stage + (new ReverbConvolverStage(response, totalResponseLength, + reverbTotalLatency, stageOffset, stageSize, + fftSize, renderPhase, + &m_accumulationBuffer)); bool isBackgroundStage = false; @@ -108,18 +120,35 @@ ReverbConvolver::ReverbConvolver(const float* impulseResponseData, size_t impuls } else m_stages.AppendElement(stage.forget()); + // Figure out next FFT size + fftSize *= 2; + stageOffset += stageSize; - ++i; - if (!useDirectConvolver) { - // Figure out next FFT size - fftSize *= 2; + if (hasRealtimeConstraint && !isBackgroundStage + && fftSize > MaxRealtimeFFTSize) { + fftSize = MaxRealtimeFFTSize; + // Custom phase positions for all but the first of the realtime + // stages of largest size. These spread out the work of the + // larger realtime stages. None of the FFTs of size 1024, 2048 or + // 4096 are performed when processing the same block. The first + // MaxRealtimeFFTSize = 4096 stage, at the end of the doubling, + // performs its FFT at block 7. The FFTs of size 2048 are + // performed in blocks 3 + 8 * n and size 1024 at 1 + 4 * n. + const uint32_t phaseLookup[] = { 14, 0, 10, 4 }; + stagePhase = WEBAUDIO_BLOCK_SIZE * + phaseLookup[m_stages.Length() % ArrayLength(phaseLookup)]; + } else if (fftSize > maxFFTSize) { + fftSize = maxFFTSize; + // A prime offset spreads out FFTs in a way that all + // available phase positions will be used if there are sufficient + // stages. + stagePhase += 5 * WEBAUDIO_BLOCK_SIZE; + } else if (stageSize > WEBAUDIO_BLOCK_SIZE) { + // As the stages are doubling in size, the next FFT will occur + // mid-way between FFTs for this stage. + stagePhase = stageSize - WEBAUDIO_BLOCK_SIZE; } - - if (hasRealtimeConstraint && !isBackgroundStage && fftSize > m_maxRealtimeFFTSize) - fftSize = m_maxRealtimeFFTSize; - if (fftSize > m_maxFFTSize) - fftSize = m_maxFFTSize; } // Start up background thread @@ -199,25 +228,16 @@ void ReverbConvolver::backgroundThreadEntry() int readIndex; while ((readIndex = m_backgroundStages[0]->inputReadIndex()) != writeIndex) { // FIXME: do better to detect buffer overrun... - // The ReverbConvolverStages need to process in amounts which evenly divide half the FFT size - const int SliceSize = MinFFTSize / 2; - // Accumulate contributions from each stage for (size_t i = 0; i < m_backgroundStages.Length(); ++i) - m_backgroundStages[i]->processInBackground(this, SliceSize); + m_backgroundStages[i]->processInBackground(this); } } } -void ReverbConvolver::process(const float* sourceChannelData, size_t sourceChannelLength, - float* destinationChannelData, size_t destinationChannelLength, - size_t framesToProcess) +void ReverbConvolver::process(const float* sourceChannelData, + float* destinationChannelData) { - bool isSafe = sourceChannelData && destinationChannelData && sourceChannelLength >= framesToProcess && destinationChannelLength >= framesToProcess; - MOZ_ASSERT(isSafe); - if (!isSafe) - return; - const float* source = sourceChannelData; float* destination = destinationChannelData; bool isDataSafe = source && destination; @@ -226,14 +246,14 @@ void ReverbConvolver::process(const float* sourceChannelData, size_t sourceChann return; // Feed input buffer (read by all threads) - m_inputBuffer.write(source, framesToProcess); + m_inputBuffer.write(source, WEBAUDIO_BLOCK_SIZE); // Accumulate contributions from each stage for (size_t i = 0; i < m_stages.Length(); ++i) - m_stages[i]->process(source, framesToProcess); + m_stages[i]->process(source); // Finally read from accumulation buffer - m_accumulationBuffer.readAndClear(destination, framesToProcess); + m_accumulationBuffer.readAndClear(destination, WEBAUDIO_BLOCK_SIZE); // Now that we've buffered more input, wake up our background thread. @@ -249,21 +269,4 @@ void ReverbConvolver::process(const float* sourceChannelData, size_t sourceChann } } -void ReverbConvolver::reset() -{ - for (size_t i = 0; i < m_stages.Length(); ++i) - m_stages[i]->reset(); - - for (size_t i = 0; i < m_backgroundStages.Length(); ++i) - m_backgroundStages[i]->reset(); - - m_accumulationBuffer.reset(); - m_inputBuffer.reset(); -} - -size_t ReverbConvolver::latencyFrames() const -{ - return 0; -} - } // namespace WebCore diff --git a/dom/media/webaudio/blink/ReverbConvolver.h b/dom/media/webaudio/blink/ReverbConvolver.h index 14b2ff2573..b7eea45b86 100644 --- a/dom/media/webaudio/blink/ReverbConvolver.h +++ b/dom/media/webaudio/blink/ReverbConvolver.h @@ -50,13 +50,13 @@ public: // For certain tweaky de-convolving applications the phase errors add up quickly and lead to non-sensical results with // larger FFT sizes and single-precision floats. In these cases 2048 is a good size. // If not doing multi-threaded convolution, then should not go > 8192. - ReverbConvolver(const float* impulseResponseData, size_t impulseResponseLength, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads); + ReverbConvolver(const float* impulseResponseData, + size_t impulseResponseLength, size_t maxFFTSize, + size_t convolverRenderPhase, bool useBackgroundThreads); ~ReverbConvolver(); - void process(const float* sourceChannelData, size_t sourceChannelLength, - float* destinationChannelData, size_t destinationChannelLength, - size_t framesToProcess); - void reset(); + void process(const float* sourceChannelData, + float* destinationChannelData); size_t impulseResponseLength() const { return m_impulseResponseLength; } @@ -65,8 +65,6 @@ public: bool useBackgroundThreads() const { return m_useBackgroundThreads; } void backgroundThreadEntry(); - size_t latencyFrames() const; - size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; private: nsTArray > m_stages; @@ -78,13 +76,6 @@ private: // One or more background threads read from this input buffer which is fed from the realtime thread. ReverbInputBuffer m_inputBuffer; - // First stage will be of size m_minFFTSize. Each next stage will be twice as big until we hit m_maxFFTSize. - size_t m_minFFTSize; - size_t m_maxFFTSize; - - // But don't exceed this size in the real-time thread (if we're doing background processing). - size_t m_maxRealtimeFFTSize; - // Background thread and synchronization base::Thread m_backgroundThread; Lock m_backgroundThreadLock; diff --git a/dom/media/webaudio/blink/ReverbConvolverStage.cpp b/dom/media/webaudio/blink/ReverbConvolverStage.cpp index fe6a0929cb..055098e887 100644 --- a/dom/media/webaudio/blink/ReverbConvolverStage.cpp +++ b/dom/media/webaudio/blink/ReverbConvolverStage.cpp @@ -37,54 +37,32 @@ using namespace mozilla; namespace WebCore { -ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, - size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer* accumulationBuffer, bool directMode) +ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t, + size_t reverbTotalLatency, + size_t stageOffset, + size_t stageLength, + size_t fftSize, size_t renderPhase, + ReverbAccumulationBuffer* accumulationBuffer) : m_accumulationBuffer(accumulationBuffer) , m_accumulationReadIndex(0) , m_inputReadIndex(0) - , m_directMode(directMode) { MOZ_ASSERT(impulseResponse); MOZ_ASSERT(accumulationBuffer); - if (!m_directMode) { - m_fftKernel = new FFTBlock(fftSize); - m_fftKernel->PadAndMakeScaledDFT(impulseResponse + stageOffset, stageLength); - m_fftConvolver = new FFTConvolver(fftSize); - } else { - m_directKernel.SetLength(fftSize / 2); - PodCopy(m_directKernel.Elements(), impulseResponse + stageOffset, fftSize / 2); - m_directConvolver = new DirectConvolver(renderSliceSize); - } - m_temporaryBuffer.SetLength(renderSliceSize); - PodZero(m_temporaryBuffer.Elements(), m_temporaryBuffer.Length()); + m_fftKernel = new FFTBlock(fftSize); + m_fftKernel->PadAndMakeScaledDFT(impulseResponse + stageOffset, stageLength); + m_fftConvolver = new FFTConvolver(fftSize, renderPhase); // The convolution stage at offset stageOffset needs to have a corresponding delay to cancel out the offset. size_t totalDelay = stageOffset + reverbTotalLatency; - // But, the FFT convolution itself incurs fftSize / 2 latency, so subtract this out... - size_t halfSize = fftSize / 2; - if (!m_directMode) { - MOZ_ASSERT(totalDelay >= halfSize); - if (totalDelay >= halfSize) - totalDelay -= halfSize; - } + // But, the FFT convolution itself incurs latency, so subtract this out... + size_t fftLatency = m_fftConvolver->latencyFrames(); + MOZ_ASSERT(totalDelay >= fftLatency); + totalDelay -= fftLatency; - // We divide up the total delay, into pre and post delay sections so that we can schedule at exactly the moment when the FFT will happen. - // This is coordinated with the other stages, so they don't all do their FFTs at the same time... - int maxPreDelayLength = std::min(halfSize, totalDelay); - m_preDelayLength = totalDelay > 0 ? renderPhase % maxPreDelayLength : 0; - if (m_preDelayLength > totalDelay) - m_preDelayLength = 0; - - m_postDelayLength = totalDelay - m_preDelayLength; - m_preReadWriteIndex = 0; - m_framesProcessed = 0; // total frames processed so far - - size_t delayBufferSize = m_preDelayLength < fftSize ? fftSize : m_preDelayLength; - delayBufferSize = delayBufferSize < renderSliceSize ? renderSliceSize : delayBufferSize; - m_preDelayBuffer.SetLength(delayBufferSize); - PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length()); + m_postDelayLength = totalDelay; } size_t ReverbConvolverStage::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const @@ -99,101 +77,31 @@ size_t ReverbConvolverStage::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSi amount += m_fftConvolver->sizeOfIncludingThis(aMallocSizeOf); } - amount += m_preDelayBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf); - amount += m_temporaryBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf); - amount += m_directKernel.ShallowSizeOfExcludingThis(aMallocSizeOf); - - if (m_directConvolver) { - amount += m_directConvolver->sizeOfIncludingThis(aMallocSizeOf); - } - return amount; } -void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_t framesToProcess) +void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver) { ReverbInputBuffer* inputBuffer = convolver->inputBuffer(); - float* source = inputBuffer->directReadFrom(&m_inputReadIndex, framesToProcess); - process(source, framesToProcess); + float* source = inputBuffer->directReadFrom(&m_inputReadIndex, + WEBAUDIO_BLOCK_SIZE); + process(source); } -void ReverbConvolverStage::process(const float* source, size_t framesToProcess) +void ReverbConvolverStage::process(const float* source) { MOZ_ASSERT(source); if (!source) return; - // Deal with pre-delay stream : note special handling of zero delay. + // Now, run the convolution (into the delay buffer). + // An expensive FFT will happen every fftSize / 2 frames. + const float* output = m_fftConvolver->process(m_fftKernel, source); - const float* preDelayedSource; - float* preDelayedDestination; - float* temporaryBuffer; - bool isTemporaryBufferSafe = false; - if (m_preDelayLength > 0) { - // Handles both the read case (call to process() ) and the write case (memcpy() ) - bool isPreDelaySafe = m_preReadWriteIndex + framesToProcess <= m_preDelayBuffer.Length(); - MOZ_ASSERT(isPreDelaySafe); - if (!isPreDelaySafe) - return; - - isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.Length(); - - preDelayedDestination = m_preDelayBuffer.Elements() + m_preReadWriteIndex; - preDelayedSource = preDelayedDestination; - temporaryBuffer = m_temporaryBuffer.Elements(); - } else { - // Zero delay - preDelayedDestination = 0; - preDelayedSource = source; - temporaryBuffer = m_preDelayBuffer.Elements(); - - isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.Length(); - } - - MOZ_ASSERT(isTemporaryBufferSafe); - if (!isTemporaryBufferSafe) - return; - - if (m_framesProcessed < m_preDelayLength) { - // For the first m_preDelayLength frames don't process the convolver, instead simply buffer in the pre-delay. - // But while buffering the pre-delay, we still need to update our index. - m_accumulationBuffer->updateReadIndex(&m_accumulationReadIndex, framesToProcess); - } else { - // Now, run the convolution (into the delay buffer). - // An expensive FFT will happen every fftSize / 2 frames. - // We process in-place here... - if (!m_directMode) - m_fftConvolver->process(m_fftKernel, preDelayedSource, temporaryBuffer, framesToProcess); - else - m_directConvolver->process(&m_directKernel, preDelayedSource, temporaryBuffer, framesToProcess); - - // Now accumulate into reverb's accumulation buffer. - m_accumulationBuffer->accumulate(temporaryBuffer, framesToProcess, &m_accumulationReadIndex, m_postDelayLength); - } - - // Finally copy input to pre-delay. - if (m_preDelayLength > 0) { - memcpy(preDelayedDestination, source, sizeof(float) * framesToProcess); - m_preReadWriteIndex += framesToProcess; - - MOZ_ASSERT(m_preReadWriteIndex <= m_preDelayLength); - if (m_preReadWriteIndex >= m_preDelayLength) - m_preReadWriteIndex = 0; - } - - m_framesProcessed += framesToProcess; -} - -void ReverbConvolverStage::reset() -{ - if (!m_directMode) - m_fftConvolver->reset(); - else - m_directConvolver->reset(); - PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length()); - m_accumulationReadIndex = 0; - m_inputReadIndex = 0; - m_framesProcessed = 0; + // Now accumulate into reverb's accumulation buffer. + m_accumulationBuffer->accumulate(output, WEBAUDIO_BLOCK_SIZE, + &m_accumulationReadIndex, + m_postDelayLength); } } // namespace WebCore diff --git a/dom/media/webaudio/blink/ReverbConvolverStage.h b/dom/media/webaudio/blink/ReverbConvolverStage.h index 73879477be..3dfe81bbdf 100644 --- a/dom/media/webaudio/blink/ReverbConvolverStage.h +++ b/dom/media/webaudio/blink/ReverbConvolverStage.h @@ -29,7 +29,6 @@ #ifndef ReverbConvolverStage_h #define ReverbConvolverStage_h -#include "DirectConvolver.h" #include "FFTConvolver.h" #include "nsTArray.h" @@ -49,14 +48,12 @@ class ReverbConvolverStage { public: // renderPhase is useful to know so that we can manipulate the pre versus post delay so that stages will perform // their heavy work (FFT processing) on different slices to balance the load in a real-time thread. - ReverbConvolverStage(const float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer*, bool directMode = false); + ReverbConvolverStage(const float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, size_t fftSize, size_t renderPhase, ReverbAccumulationBuffer*); - // WARNING: framesToProcess must be such that it evenly divides the delay buffer size (stage_offset). - void process(const float* source, size_t framesToProcess); + // |source| must point to an array of WEBAUDIO_BLOCK_SIZE elements. + void process(const float* source); - void processInBackground(ReverbConvolver* convolver, size_t framesToProcess); - - void reset(); + void processInBackground(ReverbConvolver* convolver); // Useful for background processing int inputReadIndex() const { return m_inputReadIndex; } @@ -67,22 +64,13 @@ private: nsAutoPtr m_fftKernel; nsAutoPtr m_fftConvolver; - nsTArray m_preDelayBuffer; - ReverbAccumulationBuffer* m_accumulationBuffer; int m_accumulationReadIndex; int m_inputReadIndex; - size_t m_preDelayLength; size_t m_postDelayLength; - size_t m_preReadWriteIndex; - size_t m_framesProcessed; nsTArray m_temporaryBuffer; - - bool m_directMode; - nsTArray m_directKernel; - nsAutoPtr m_directConvolver; }; } // namespace WebCore diff --git a/dom/media/webaudio/blink/moz.build b/dom/media/webaudio/blink/moz.build index dde8d01e30..fd622d0749 100644 --- a/dom/media/webaudio/blink/moz.build +++ b/dom/media/webaudio/blink/moz.build @@ -6,7 +6,6 @@ UNIFIED_SOURCES += [ 'Biquad.cpp', - 'DirectConvolver.cpp', 'DynamicsCompressor.cpp', 'DynamicsCompressorKernel.cpp', 'FFTConvolver.cpp', @@ -30,6 +29,3 @@ FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '/dom/media/webaudio', ] - -if not CONFIG['GNU_CXX']: - ALLOW_COMPILER_WARNINGS = True diff --git a/dom/media/webaudio/test/test_periodicWave.html b/dom/media/webaudio/test/test_periodicWave.html index 6ea5f06dec..3ed4407486 100644 --- a/dom/media/webaudio/test/test_periodicWave.html +++ b/dom/media/webaudio/test/test_periodicWave.html @@ -37,9 +37,9 @@ addLoadEvent(function() { expectException(function() { ac.createPeriodicWave(new Float32Array(0), new Float32Array(0)); }, DOMException.NOT_SUPPORTED_ERR); - expectException(function() { + expectNoException(function() { ac.createPeriodicWave(new Float32Array(4097), new Float32Array(4097)); - }, DOMException.NOT_SUPPORTED_ERR); + }); runTest(); }); diff --git a/dom/media/webm/WebMReader.cpp b/dom/media/webm/WebMReader.cpp index 5b2b6d4636..3897e5cbc0 100644 --- a/dom/media/webm/WebMReader.cpp +++ b/dom/media/webm/WebMReader.cpp @@ -403,17 +403,13 @@ WebMReader::RetrieveWebMMetadata(MediaInfo* aInfo) } } + mInfo.mMediaSeekable = nestegg_has_cues(mContext); + *aInfo = mInfo; return NS_OK; } -bool -WebMReader::IsMediaSeekable() -{ - return mContext && nestegg_has_cues(mContext); -} - bool WebMReader::DecodeAudioPacket(NesteggPacketHolder* aHolder) { MOZ_ASSERT(OnTaskQueue()); diff --git a/dom/media/webm/WebMReader.h b/dom/media/webm/WebMReader.h index 9f4a64db91..d974e1f7cb 100644 --- a/dom/media/webm/WebMReader.h +++ b/dom/media/webm/WebMReader.h @@ -92,8 +92,6 @@ public: virtual media::TimeIntervals GetBuffered() override; - virtual bool IsMediaSeekable() override; - // Value passed to NextPacket to determine if we are reading a video or an // audio packet. enum TrackType { diff --git a/dom/svg/SVGContentUtils.h b/dom/svg/SVGContentUtils.h index 2136c5e6e7..f050e7d468 100644 --- a/dom/svg/SVGContentUtils.h +++ b/dom/svg/SVGContentUtils.h @@ -8,7 +8,6 @@ #define MOZILLA_SVGCONTENTUTILS_H // include math.h to pick up definition of M_ maths defines e.g. M_PI -#define _USE_MATH_DEFINES #include #include "mozilla/gfx/2D.h" // for StrokeOptions diff --git a/dom/system/gonk/GeolocationUtil.cpp b/dom/system/gonk/GeolocationUtil.cpp new file mode 100644 index 0000000000..99d484a19c --- /dev/null +++ b/dom/system/gonk/GeolocationUtil.cpp @@ -0,0 +1,28 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GeolocationUtil.h" + +double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon) +{ + // Use spherical law of cosines to calculate difference + // Not quite as correct as the Haversine but simpler and cheaper + const double radsInDeg = M_PI / 180.0; + const double rNewLat = aLat * radsInDeg; + const double rNewLon = aLon * radsInDeg; + const double rOldLat = aLastLat * radsInDeg; + const double rOldLon = aLastLon * radsInDeg; + // WGS84 equatorial radius of earth = 6378137m + double cosDelta = (sin(rNewLat) * sin(rOldLat)) + + (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon)); + if (cosDelta > 1.0) { + cosDelta = 1.0; + } else if (cosDelta < -1.0) { + cosDelta = -1.0; + } + return acos(cosDelta) * 6378137; +} + diff --git a/dom/system/gonk/GeolocationUtil.h b/dom/system/gonk/GeolocationUtil.h new file mode 100644 index 0000000000..fde337fb86 --- /dev/null +++ b/dom/system/gonk/GeolocationUtil.h @@ -0,0 +1,13 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GEOLOCATIONUTIL_H +#define GEOLOCATIONUTIL_H + +double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon); + +#endif + diff --git a/dom/system/gonk/GonkGPSGeolocationProvider.cpp b/dom/system/gonk/GonkGPSGeolocationProvider.cpp index 57f23b9c4f..c17ac15d71 100644 --- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp +++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp @@ -15,12 +15,13 @@ */ #include "GonkGPSGeolocationProvider.h" -#include "mozstumbler/MozStumbler.h" +#include #include #include -#include "mozilla/Constants.h" +#include "GeolocationUtil.h" +#include "mozstumbler/MozStumbler.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "nsContentUtils.h" @@ -38,6 +39,7 @@ #include "mozilla/dom/SettingChangeNotificationBinding.h" #ifdef MOZ_B2G_RIL +#include "mozstumbler/MozStumbler.h" #include "nsIIccInfo.h" #include "nsIMobileConnectionInfo.h" #include "nsIMobileConnectionService.h" @@ -88,71 +90,6 @@ AGpsCallbacks GonkGPSGeolocationProvider::mAGPSCallbacks; AGpsRilCallbacks GonkGPSGeolocationProvider::mAGPSRILCallbacks; #endif // MOZ_B2G_RIL -double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon) -{ - // Use spherical law of cosines to calculate difference - // Not quite as correct as the Haversine but simpler and cheaper - const double radsInDeg = M_PI / 180.0; - const double rNewLat = aLat * radsInDeg; - const double rNewLon = aLon * radsInDeg; - const double rOldLat = aLastLat * radsInDeg; - const double rOldLon = aLastLon * radsInDeg; - // WGS84 equatorial radius of earth = 6378137m - double cosDelta = (sin(rNewLat) * sin(rOldLat)) + - (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon)); - if (cosDelta > 1.0) { - cosDelta = 1.0; - } else if (cosDelta < -1.0) { - cosDelta = -1.0; - } - return acos(cosDelta) * 6378137; -} - -class RequestCellInfoEvent : public nsRunnable { - public: - RequestCellInfoEvent(StumblerInfo *callback) - : mRequestCallback(callback) - {} - - NS_IMETHOD Run() { - MOZ_ASSERT(NS_IsMainThread()); - // Get Cell Info - nsCOMPtr service = - do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID); - - if (!service) { - nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIMobileConnectionService \n"); - return NS_OK; - } - nsCOMPtr connection; - uint32_t numberOfRilServices = 1, cellInfoNum = 0; - - service->GetNumItems(&numberOfRilServices); - for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) { - service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection)); - if (!connection) { - nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum); - } else { - cellInfoNum++; - connection->GetCellInfoList(mRequestCallback); - } - } - mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum); - - // Get Wifi AP Info - nsCOMPtr ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1"); - nsCOMPtr wifi = do_GetInterface(ir); - if (!wifi) { - mRequestCallback->SetWifiInfoResponseReceived(); - nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIWifi interface\n"); - return NS_OK; - } - wifi->GetWifiScanResults(mRequestCallback); - return NS_OK; - } - private: - RefPtr mRequestCallback; -}; void GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location) @@ -182,6 +119,11 @@ GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location) MOZ_ASSERT(location); + const float kImpossibleAccuracy_m = 0.001; + if (location->accuracy < kImpossibleAccuracy_m) { + return; + } + RefPtr somewhere = new nsGeoPosition(location->latitude, location->longitude, location->altitude, @@ -206,35 +148,9 @@ GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location) RefPtr event = new UpdateLocationEvent(somewhere); NS_DispatchToMainThread(event); - const double kMinChangeInMeters = 30; - static int64_t lastTime_ms = 0; - static double sLastLat = 0; - static double sLastLon = 0; - double delta = -1.0; - int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms; - - if (0 != sLastLon || 0 != sLastLat) { - delta = CalculateDeltaInMeter(location->latitude, location->longitude, sLastLat, sLastLon); - } - if (gDebug_isLoggingEnabled) { - nsContentUtils::LogMessageToConsole("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n", - location->longitude, location->latitude, timediff, delta); - } - - // Consecutive GPS locations must be 30 meters and 3 seconds apart - if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){ - lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC); - sLastLat = location->latitude; - sLastLon = location->longitude; - RefPtr requestCallback = new StumblerInfo(somewhere); - RefPtr runnable = new RequestCellInfoEvent(requestCallback); - NS_DispatchToMainThread(runnable); - } else { - if (gDebug_isLoggingEnabled) { - nsContentUtils::LogMessageToConsole( - "Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n"); - } - } +#ifdef MOZ_B2G_RIL + MozStumble(somewhere); +#endif } void @@ -680,6 +596,33 @@ GonkGPSGeolocationProvider::RequestSetID(uint32_t flags) mAGpsRilInterface->set_set_id(type, idBytes.get()); } +namespace { +int +ConvertToGpsRefLocationType(const nsAString& aConnectionType) +{ + const char* GSM_TYPES[] = { "gsm", "gprs", "edge" }; + const char* UMTS_TYPES[] = { "umts", "hspda", "hsupa", "hspa", "hspa+" }; + + for (auto type: GSM_TYPES) { + if (aConnectionType.EqualsASCII(type)) { + return AGPS_REF_LOCATION_TYPE_GSM_CELLID; + } + } + + for (auto type: UMTS_TYPES) { + if (aConnectionType.EqualsASCII(type)) { + return AGPS_REF_LOCATION_TYPE_UMTS_CELLID; + } + } + + if (gDebug_isLoggingEnabled) { + nsContentUtils::LogMessageToConsole("geo: Unsupported connection type %s\n", + NS_ConvertUTF16toUTF8(aConnectionType).get()); + } + return AGPS_REF_LOCATION_TYPE_GSM_CELLID; +} +} // namespace + void GonkGPSGeolocationProvider::SetReferenceLocation() { @@ -692,9 +635,6 @@ GonkGPSGeolocationProvider::SetReferenceLocation() AGpsRefLocation location; - // TODO: Bug 772750 - get mobile connection technology from rilcontext - location.type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID; - nsCOMPtr service = do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID); if (!service) { @@ -703,14 +643,19 @@ GonkGPSGeolocationProvider::SetReferenceLocation() } nsCOMPtr connection; - // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in - // MultiSIM configuration - service->GetItemByServiceId(0 /* Client Id */, getter_AddRefs(connection)); + service->GetItemByServiceId(mRilDataServiceId, getter_AddRefs(connection)); NS_ENSURE_TRUE_VOID(connection); nsCOMPtr voice; connection->GetVoice(getter_AddRefs(voice)); if (voice) { + nsAutoString connectionType; + nsresult rv = voice->GetType(connectionType); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + location.type = ConvertToGpsRefLocationType(connectionType); + nsCOMPtr networkInfo; voice->GetNetwork(getter_AddRefs(networkInfo)); if (networkInfo) { @@ -1181,9 +1126,7 @@ GonkGPSGeolocationProvider::Observe(nsISupports* aSubject, } nsCOMPtr connection; - // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in - // MultiSIM configuration - service->GetItemByServiceId(0 /* Client Id */, getter_AddRefs(connection)); + service->GetItemByServiceId(mRilDataServiceId, getter_AddRefs(connection)); if (!connection) { break; } diff --git a/dom/system/gonk/moz.build b/dom/system/gonk/moz.build index af4b0b46df..b2b83867d8 100644 --- a/dom/system/gonk/moz.build +++ b/dom/system/gonk/moz.build @@ -33,8 +33,8 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'dom_system_gonk' EXPORTS += [ + 'GeolocationUtil.h', 'GonkGPSGeolocationProvider.h', - 'mozstumbler/MozStumbler.h', 'nsVolume.h', 'nsVolumeService.h', ] @@ -43,14 +43,11 @@ UNIFIED_SOURCES += [ 'AudioManager.cpp', 'AutoMounter.cpp', 'AutoMounterSetting.cpp', + 'GeolocationUtil.cpp', 'GonkGPSGeolocationProvider.cpp', 'MozMtpDatabase.cpp', 'MozMtpServer.cpp', 'MozMtpStorage.cpp', - 'mozstumbler/MozStumbler.cpp', - 'mozstumbler/StumblerLogging.cpp', - 'mozstumbler/UploadStumbleRunnable.cpp', - 'mozstumbler/WriteStumbleOnThread.cpp', 'NetIdManager.cpp', 'NetworkUtils.cpp', 'NetworkWorker.cpp', @@ -93,6 +90,15 @@ EXTRA_JS_MODULES += [ ] if CONFIG['MOZ_B2G_RIL']: + EXPORTS += [ + 'mozstumbler/MozStumbler.h', + ] + UNIFIED_SOURCES += [ + 'mozstumbler/MozStumbler.cpp', + 'mozstumbler/StumblerLogging.cpp', + 'mozstumbler/UploadStumbleRunnable.cpp', + 'mozstumbler/WriteStumbleOnThread.cpp' + ] XPIDL_SOURCES += [ 'nsIDataCallInterfaceService.idl', 'nsIDataCallManager.idl', diff --git a/dom/system/gonk/mozstumbler/MozStumbler.cpp b/dom/system/gonk/mozstumbler/MozStumbler.cpp index e597be7188..8a43f3633b 100644 --- a/dom/system/gonk/mozstumbler/MozStumbler.cpp +++ b/dom/system/gonk/mozstumbler/MozStumbler.cpp @@ -5,12 +5,22 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MozStumbler.h" +#include "nsDataHashtable.h" #include "nsGeoPosition.h" +#include "nsNetCID.h" #include "nsPrintfCString.h" #include "StumblerLogging.h" #include "WriteStumbleOnThread.h" -#include "nsNetCID.h" -#include "nsDataHashtable.h" +#include "../GeolocationUtil.h" + +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIMobileConnectionInfo.h" +#include "nsIMobileConnectionService.h" +#include "nsIMobileCellInfo.h" +#include "nsIMobileNetworkInfo.h" +#include "nsINetworkInterface.h" +#include "nsIRadioInterfaceLayer.h" using namespace mozilla; using namespace mozilla::dom; @@ -18,6 +28,91 @@ using namespace mozilla::dom; NS_IMPL_ISUPPORTS(StumblerInfo, nsICellInfoListCallback, nsIWifiScanResultsReady) +class RequestCellInfoEvent : public nsRunnable { +public: + RequestCellInfoEvent(StumblerInfo *callback) + : mRequestCallback(callback) + {} + + NS_IMETHOD Run() { + MOZ_ASSERT(NS_IsMainThread()); + // Get Cell Info + nsCOMPtr service = + do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID); + + if (!service) { + STUMBLER_ERR("Stumbler-can not get nsIMobileConnectionService \n"); + return NS_OK; + } + nsCOMPtr connection; + uint32_t numberOfRilServices = 1, cellInfoNum = 0; + + service->GetNumItems(&numberOfRilServices); + for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) { + service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection)); + if (!connection) { + STUMBLER_ERR("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum); + } else { + cellInfoNum++; + connection->GetCellInfoList(mRequestCallback); + } + } + mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum); + + // Get Wifi AP Info + nsCOMPtr ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1"); + nsCOMPtr wifi = do_GetInterface(ir); + if (!wifi) { + mRequestCallback->SetWifiInfoResponseReceived(); + STUMBLER_ERR("Stumbler-can not get nsIWifi interface\n"); + return NS_OK; + } + wifi->GetWifiScanResults(mRequestCallback); + return NS_OK; + } +private: + RefPtr mRequestCallback; +}; + +void +MozStumble(nsGeoPosition* position) +{ + nsCOMPtr coords; + position->GetCoords(getter_AddRefs(coords)); + if (!coords) { + return; + } + + double latitude, longitude; + coords->GetLatitude(&latitude); + coords->GetLongitude(&longitude); + + const double kMinChangeInMeters = 30; + static int64_t lastTime_ms = 0; + static double sLastLat = 0; + static double sLastLon = 0; + double delta = -1.0; + int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms; + + if (0 != sLastLon || 0 != sLastLat) { + delta = CalculateDeltaInMeter(latitude, longitude, sLastLat, sLastLon); + } + STUMBLER_DBG("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n", + longitude, latitude, timediff, delta); + + // Consecutive GPS locations must be 30 meters and 3 seconds apart + if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){ + lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC); + sLastLat = latitude; + sLastLon = longitude; + RefPtr requestCallback = new StumblerInfo(position); + RefPtr runnable = new RequestCellInfoEvent(requestCallback); + NS_DispatchToMainThread(runnable); + } else { + STUMBLER_DBG("Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n"); + } +} + void StumblerInfo::SetWifiInfoResponseReceived() { diff --git a/dom/system/gonk/mozstumbler/MozStumbler.h b/dom/system/gonk/mozstumbler/MozStumbler.h index 880b365251..41ee4e5e14 100644 --- a/dom/system/gonk/mozstumbler/MozStumbler.h +++ b/dom/system/gonk/mozstumbler/MozStumbler.h @@ -15,6 +15,8 @@ class nsGeoPosition; +void MozStumble(nsGeoPosition* position); + class StumblerInfo final : public nsICellInfoListCallback, public nsIWifiScanResultsReady { diff --git a/dom/webidl/DominatorTree.webidl b/dom/webidl/DominatorTree.webidl index e77bfce5c4..32bed41101 100644 --- a/dom/webidl/DominatorTree.webidl +++ b/dom/webidl/DominatorTree.webidl @@ -53,4 +53,12 @@ interface DominatorTree { */ [Throws] NodeSize? getRetainedSize(NodeId node); + + /** + * Get the set of ids of nodes immediately dominated by the node with the + * given id. The resulting array is sorted by greatest to least retained + * size. If given an invalid id, null is returned. Throws an error on OOM. + */ + [Throws] + sequence? getImmediatelyDominated(NodeId node); }; diff --git a/extensions/moz.build b/extensions/moz.build index a719535ead..ddc1271d9e 100644 --- a/extensions/moz.build +++ b/extensions/moz.build @@ -4,4 +4,4 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -DIRS += CONFIG['MOZ_EXTENSIONS'].split() +DIRS += CONFIG['MOZ_EXTENSIONS'] diff --git a/gfx/2d/Blur.cpp b/gfx/2d/Blur.cpp index 2618e2b1ec..a2398d0b3c 100644 --- a/gfx/2d/Blur.cpp +++ b/gfx/2d/Blur.cpp @@ -11,7 +11,6 @@ #include #include "mozilla/CheckedInt.h" -#include "mozilla/Constants.h" #include "2D.h" #include "DataSurfaceHelpers.h" diff --git a/gfx/2d/DrawCommand.h b/gfx/2d/DrawCommand.h index 135548b9c6..eb303dd585 100644 --- a/gfx/2d/DrawCommand.h +++ b/gfx/2d/DrawCommand.h @@ -6,8 +6,6 @@ #ifndef MOZILLA_GFX_DRAWCOMMAND_H_ #define MOZILLA_GFX_DRAWCOMMAND_H_ - -#define _USE_MATH_DEFINES #include #include "2D.h" @@ -592,5 +590,4 @@ public: } // namespace mozilla - #endif /* MOZILLA_GFX_DRAWCOMMAND_H_ */ diff --git a/gfx/2d/DrawTargetD2D.cpp b/gfx/2d/DrawTargetD2D.cpp index e60dcef9b2..f1e2731090 100644 --- a/gfx/2d/DrawTargetD2D.cpp +++ b/gfx/2d/DrawTargetD2D.cpp @@ -16,12 +16,12 @@ #include "Logging.h" #include "Tools.h" #include -#include "mozilla/Constants.h" #include "FilterNodeSoftware.h" #include "FilterNodeD2D1.h" #include "ExtendInputEffectD2D1.h" +#include #include // decltype is not usable for overloaded functions. diff --git a/gfx/2d/FilterNodeSoftware.cpp b/gfx/2d/FilterNodeSoftware.cpp index 54422ad2aa..17b58e97f5 100644 --- a/gfx/2d/FilterNodeSoftware.cpp +++ b/gfx/2d/FilterNodeSoftware.cpp @@ -3,8 +3,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#define _USE_MATH_DEFINES - #include #include "DataSurfaceHelpers.h" #include "FilterNodeSoftware.h" diff --git a/gfx/2d/PathD2D.cpp b/gfx/2d/PathD2D.cpp index 69706c79d4..59e7e82c14 100644 --- a/gfx/2d/PathD2D.cpp +++ b/gfx/2d/PathD2D.cpp @@ -8,7 +8,6 @@ #include #include "DrawTargetD2D.h" #include "Logging.h" -#include "mozilla/Constants.h" namespace mozilla { namespace gfx { diff --git a/gfx/2d/PathHelpers.cpp b/gfx/2d/PathHelpers.cpp index a7bf5b46e7..49c344b427 100644 --- a/gfx/2d/PathHelpers.cpp +++ b/gfx/2d/PathHelpers.cpp @@ -3,9 +3,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#define _USE_MATH_DEFINES -#include - #include "PathHelpers.h" namespace mozilla { @@ -278,4 +275,3 @@ MaxStrokeExtents(const StrokeOptions& aStrokeOptions, } // namespace gfx } // namespace mozilla - diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h index b7636bf6e8..cf899c5f25 100644 --- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -7,7 +7,6 @@ #define MOZILLA_GFX_PATHHELPERS_H_ #include "2D.h" -#include "mozilla/Constants.h" #include "UserData.h" #include diff --git a/gfx/2d/image_operations.cpp b/gfx/2d/image_operations.cpp index 7d9b44da04..d25ff304fb 100644 --- a/gfx/2d/image_operations.cpp +++ b/gfx/2d/image_operations.cpp @@ -28,7 +28,6 @@ #include "base/basictypes.h" -#define _USE_MATH_DEFINES #include #include #include diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build index 6634a2b0da..d152fb9889 100644 --- a/gfx/2d/moz.build +++ b/gfx/2d/moz.build @@ -152,6 +152,7 @@ UNIFIED_SOURCES += [ 'Matrix.cpp', 'Path.cpp', 'PathCairo.cpp', + 'PathHelpers.cpp', 'PathRecording.cpp', 'Quaternion.cpp', 'RecordedEvent.cpp', @@ -162,10 +163,6 @@ UNIFIED_SOURCES += [ 'SourceSurfaceRawData.cpp', ] -SOURCES += [ - 'PathHelpers.cpp', # Uses _USE_MATH_DEFINES -] - if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': EXPORTS.mozilla.gfx += [ 'QuartzSupport.h', diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 8737fde680..78060b5dbc 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -29,7 +29,6 @@ #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/BasicEvents.h" // for Modifiers, MODIFIER_* #include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown -#include "mozilla/Constants.h" // for M_PI #include "mozilla/EventForwards.h" // for nsEventStatus_* #include "mozilla/Preferences.h" // for Preferences #include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index ff1c4c65d3..47a85ad9ba 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -3,9 +3,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifdef _MSC_VER -#define _USE_MATH_DEFINES -#endif #include #include "mozilla/Alignment.h" diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index 2a96f23e8e..7f630b2834 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -6,9 +6,9 @@ #ifndef GFX_PREFS_H #define GFX_PREFS_H +#include // for M_PI #include #include "mozilla/Assertions.h" -#include "mozilla/Constants.h" // for M_PI // First time gfxPrefs::GetSingleton() needs to be called on the main thread, // before any of the methods accessing the values are used, but after diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index d6f760e092..08ced68185 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -413,6 +413,7 @@ struct DefaultGCPolicy }; template <> struct DefaultGCPolicy : public IgnoreGCPolicy {}; +template <> struct DefaultGCPolicy : public IgnoreGCPolicy {}; template struct DefaultGCPolicy> diff --git a/js/public/TrackedOptimizationInfo.h b/js/public/TrackedOptimizationInfo.h index 7f1670d276..ee99312787 100644 --- a/js/public/TrackedOptimizationInfo.h +++ b/js/public/TrackedOptimizationInfo.h @@ -26,6 +26,7 @@ namespace JS { _(GetProp_Innerize) \ _(GetProp_InlineCache) \ _(GetProp_SharedCache) \ + _(GetProp_ModuleNamespace) \ \ _(SetProp_CommonSetter) \ _(SetProp_TypedObject) \ @@ -104,6 +105,8 @@ namespace JS { _(NoSimdJitSupport) \ _(SimdTypeNotOptimized) \ _(UnknownSimdProperty) \ + _(NotModuleNamespace) \ + _(UnknownProperty) \ \ _(ICOptStub_GenericSuccess) \ \ diff --git a/js/public/UbiNodeDominatorTree.h b/js/public/UbiNodeDominatorTree.h index 08593030e8..f8f31b551f 100644 --- a/js/public/UbiNodeDominatorTree.h +++ b/js/public/UbiNodeDominatorTree.h @@ -141,6 +141,11 @@ class JS_EXPORT_API(DominatorTree) return DominatedNodePtr(postOrder, endPtr); } + size_t length() const { + MOZ_ASSERT(beginPtr <= endPtr); + return endPtr - beginPtr; + } + /** * Safely skip ahead `n` dominators in the range, in O(1) time. * diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index 36f09ad64e..d3853d13dd 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -1808,12 +1808,9 @@ AsmJSModule::setProfilingEnabled(JSContext* cx, bool enabled) MOZ_CRASH(); void* callee = nullptr; (void)callerRetAddr; -#elif defined(JS_CODEGEN_MIPS32) - Instruction* instr = (Instruction*)(callerRetAddr - 4 * sizeof(uint32_t)); - void* callee = (void*)Assembler::ExtractLuiOriValue(instr, instr->next()); -#elif defined(JS_CODEGEN_MIPS64) - Instruction* instr = (Instruction*)(callerRetAddr - 6 * sizeof(uint32_t)); - void* callee = (void*)Assembler::ExtractLoad64Value(instr); +#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + uint8_t* instr = callerRetAddr - Assembler::PatchWrite_NearCallSize(); + void* callee = (void*)Assembler::ExtractInstructionImmediate(instr); #elif defined(JS_CODEGEN_NONE) MOZ_CRASH(); void* callee = nullptr; @@ -1838,13 +1835,8 @@ AsmJSModule::setProfilingEnabled(JSContext* cx, bool enabled) #elif defined(JS_CODEGEN_ARM64) (void)newCallee; MOZ_CRASH(); -#elif defined(JS_CODEGEN_MIPS32) - Assembler::WriteLuiOriInstructions(instr, instr->next(), - ScratchRegister, (uint32_t)newCallee); - instr[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); -#elif defined(JS_CODEGEN_MIPS64) - Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)newCallee); - instr[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); +#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + Assembler::PatchInstructionImmediate(instr, PatchedImmPtr(newCallee)); #elif defined(JS_CODEGEN_NONE) MOZ_CRASH(); #else diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 3c640afcb8..50bea4f3eb 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -218,7 +218,7 @@ MapIteratorObject::next(JSContext* cx, Handle mapIterator, const Class MapObject::class_ = { "Map", - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Map), nullptr, // addProperty nullptr, // delProperty @@ -409,21 +409,20 @@ MapObject::set(JSContext* cx, HandleObject obj, HandleValue k, HandleValue v) } MapObject* -MapObject::create(JSContext* cx) +MapObject::create(JSContext* cx, HandleObject proto /* = nullptr */) { - Rooted obj(cx, NewBuiltinClassInstance(cx)); - if (!obj) - return nullptr; - - ValueMap* map = cx->new_(cx->runtime()); + auto map = cx->make_unique(cx->runtime()); if (!map || !map->init()) { - js_delete(map); ReportOutOfMemory(cx); return nullptr; } - obj->setPrivate(map); - return obj; + MapObject* mapObj = NewObjectWithClassProto(cx, proto); + if (!mapObj) + return nullptr; + + mapObj->setPrivate(map.release()); + return mapObj; } void @@ -441,7 +440,12 @@ MapObject::construct(JSContext* cx, unsigned argc, Value* vp) if (!ThrowIfNotConstructing(cx, args, "Map")) return false; - Rooted obj(cx, MapObject::create(cx)); + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted obj(cx, MapObject::create(cx, proto)); if (!obj) return false; @@ -1058,19 +1062,19 @@ SetObject::add(JSContext* cx, HandleObject obj, HandleValue k) } SetObject* -SetObject::create(JSContext* cx) +SetObject::create(JSContext* cx, HandleObject proto /* = nullptr */) { - SetObject* obj = NewBuiltinClassInstance(cx); - if (!obj) - return nullptr; - - ValueSet* set = cx->new_(cx->runtime()); + auto set = cx->make_unique(cx->runtime()); if (!set || !set->init()) { - js_delete(set); ReportOutOfMemory(cx); return nullptr; } - obj->setPrivate(set); + + SetObject* obj = NewObjectWithClassProto(cx, proto); + if (!obj) + return nullptr; + + obj->setPrivate(set.release()); return obj; } @@ -1100,7 +1104,12 @@ SetObject::construct(JSContext* cx, unsigned argc, Value* vp) if (!ThrowIfNotConstructing(cx, args, "Set")) return false; - Rooted obj(cx, SetObject::create(cx)); + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted obj(cx, SetObject::create(cx, proto)); if (!obj) return false; diff --git a/js/src/builtin/MapObject.h b/js/src/builtin/MapObject.h index 835905594d..1118675f87 100644 --- a/js/src/builtin/MapObject.h +++ b/js/src/builtin/MapObject.h @@ -92,7 +92,7 @@ class MapObject : public NativeObject { JS::AutoValueVector* entries); static bool entries(JSContext* cx, unsigned argc, Value* vp); static bool has(JSContext* cx, unsigned argc, Value* vp); - static MapObject* create(JSContext* cx); + static MapObject* create(JSContext* cx, HandleObject proto = nullptr); // Publicly exposed Map calls for JSAPI access (webidl maplike/setlike // interfaces, etc.) @@ -181,7 +181,7 @@ class SetObject : public NativeObject { // Publicly exposed Set calls for JSAPI access (webidl maplike/setlike // interfaces, etc.) - static SetObject* create(JSContext *cx); + static SetObject* create(JSContext *cx, HandleObject proto = nullptr); static uint32_t size(JSContext *cx, HandleObject obj); static bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval); static bool clear(JSContext *cx, HandleObject obj); diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 750ea0af77..9f470daeff 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -275,6 +275,7 @@ ModuleNamespaceObject::create(JSContext* cx, HandleModuleObject module) RootedValue priv(cx, ObjectValue(*module)); ProxyOptions options; options.setLazyProto(true); + options.setSingleton(true); RootedObject object(cx, NewProxyObject(cx, &proxyHandler, priv, nullptr, options)); if (!object) return nullptr; diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 09b4725986..7500b9aff4 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -33,7 +33,12 @@ js::obj_construct(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); RootedObject obj(cx, nullptr); - if (args.length() > 0 && !args[0].isNullOrUndefined()) { + if (args.isConstructing() && (&args.newTarget().toObject() != &args.callee())) { + RootedObject newTarget(cx, &args.newTarget().toObject()); + obj = CreateThis(cx, &PlainObject::class_, newTarget); + if (!obj) + return false; + } else if (args.length() > 0 && !args[0].isNullOrUndefined()) { obj = ToObject(cx, args[0]); if (!obj) return false; diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 7db8cbed63..3df7d4ec25 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -185,7 +185,7 @@ RegExpInitialize(JSContext* cx, Handle obj, HandleValue patternVa } /* Steps 11-15. */ - if (!InitializeRegExp(cx, obj, pattern, flags)) + if (!RegExpObject::initFromAtom(cx, obj, pattern, flags)) return false; /* Step 16. */ @@ -268,7 +268,7 @@ regexp_compile_impl(JSContext* cx, const CallArgs& args) } // Step 5. - if (!InitializeRegExp(cx, regexp, sourceAtom, flags)) + if (!RegExpObject::initFromAtom(cx, regexp, sourceAtom, flags)) return false; args.rval().setObject(*regexp); @@ -307,11 +307,11 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) if (!IsRegExp(cx, args.get(0), &patternIsRegExp)) return false; - if (args.isConstructing()) { - // XXX Step 3! - } else { - // XXX Step 4a + // We can delay step 3 and step 4a until later, during + // GetPrototypeFromCallableConstructor calls. Accessing the new.target + // and the callee from the stack is unobservable. + if (!args.isConstructing()) { // Step 4b. if (patternIsRegExp && !args.hasDefined(1)) { RootedObject patternObj(cx, &args[0].toObject()); @@ -341,6 +341,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) // don't reuse the RegExpShared below. RootedObject patternObj(cx, &patternValue.toObject()); + // Step 5 RootedAtom sourceAtom(cx); RegExpFlag flags; { @@ -353,27 +354,30 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) if (!args.hasDefined(1)) { // Step 5b. flags = g->getFlags(); - } else { - // Step 5c. - // XXX We shouldn't be converting to string yet! This must - // come *after* the .constructor access in step 8. - flags = RegExpFlag(0); - RootedString flagStr(cx, ToString(cx, args[1])); - if (!flagStr) - return false; - if (!ParseRegExpFlags(cx, flagStr, &flags)) - return false; } } // Steps 8-9. - // XXX Note bug in step 5c, with respect to step 8. - Rooted regexp(cx, RegExpAlloc(cx)); + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + + Rooted regexp(cx, RegExpAlloc(cx, proto)); if (!regexp) return false; // Step 10. - if (!InitializeRegExp(cx, regexp, sourceAtom, flags)) + if (args.hasDefined(1)) { + // Step 5c / 21.2.3.2.2 RegExpInitialize step 5. + flags = RegExpFlag(0); + RootedString flagStr(cx, ToString(cx, args[1])); + if (!flagStr) + return false; + if (!ParseRegExpFlags(cx, flagStr, &flags)) + return false; + } + + if (!RegExpObject::initFromAtom(cx, regexp, sourceAtom, flags)) return false; args.rval().setObject(*regexp); @@ -404,7 +408,11 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) } // Steps 8-9. - Rooted regexp(cx, RegExpAlloc(cx)); + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + + Rooted regexp(cx, RegExpAlloc(cx, proto)); if (!regexp) return false; @@ -699,7 +707,9 @@ js::CreateRegExpPrototype(JSContext* cx, JSProtoKey key) proto->NativeObject::setPrivate(nullptr); RootedAtom source(cx, cx->names().empty); - return InitializeRegExp(cx, proto, source, RegExpFlag(0)); + if (!RegExpObject::initFromAtom(cx, proto, source, RegExpFlag(0))) + return nullptr; + return proto; } static bool diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index c290fcc68d..2efeb0a519 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -1480,7 +1480,7 @@ EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); #ifdef CHECK_OSIPOINT_REGISTERS - jit::js_JitOptions.checkOsiPointRegisters = true; + jit::JitOptions.checkOsiPointRegisters = true; #endif args.rval().setUndefined(); return true; @@ -1774,7 +1774,7 @@ static bool SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - jit::js_JitOptions.checkGraphConsistency = ToBoolean(args.get(0)); + jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0)); args.rval().setUndefined(); return true; } diff --git a/js/src/builtin/WeakMapObject.cpp b/js/src/builtin/WeakMapObject.cpp index 240398b71b..f948728538 100644 --- a/js/src/builtin/WeakMapObject.cpp +++ b/js/src/builtin/WeakMapObject.cpp @@ -316,7 +316,8 @@ WeakMap_construct(JSContext* cx, unsigned argc, Value* vp) if (!ThrowIfNotConstructing(cx, args, "WeakMap")) return false; - RootedObject obj(cx, NewBuiltinClassInstance(cx, &WeakMapObject::class_)); + RootedObject newTarget(cx, &args.newTarget().toObject()); + RootedObject obj(cx, CreateThis(cx, &WeakMapObject::class_, newTarget)); if (!obj) return false; diff --git a/js/src/builtin/WeakSetObject.cpp b/js/src/builtin/WeakSetObject.cpp index 35fb216e14..6ae866c236 100644 --- a/js/src/builtin/WeakSetObject.cpp +++ b/js/src/builtin/WeakSetObject.cpp @@ -61,14 +61,14 @@ WeakSetObject::initClass(JSContext* cx, JSObject* obj) } WeakSetObject* -WeakSetObject::create(JSContext* cx) +WeakSetObject::create(JSContext* cx, HandleObject proto /* = nullptr */) { - Rooted obj(cx, NewBuiltinClassInstance(cx)); - if (!obj) + RootedObject map(cx, NewBuiltinClassInstance(cx)); + if (!map) return nullptr; - RootedObject map(cx, JS::NewWeakMapObject(cx)); - if (!map) + WeakSetObject* obj = NewObjectWithClassProto(cx, proto); + if (!obj) return nullptr; obj->setReservedSlot(WEAKSET_MAP_SLOT, ObjectValue(*map)); @@ -78,16 +78,21 @@ WeakSetObject::create(JSContext* cx) bool WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp) { - Rooted obj(cx, WeakSetObject::create(cx)); - if (!obj) - return false; - // Based on our "Set" implementation instead of the more general ES6 steps. CallArgs args = CallArgsFromVp(argc, vp); if (!ThrowIfNotConstructing(cx, args, "WeakSet")) return false; + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted obj(cx, WeakSetObject::create(cx, proto)); + if (!obj) + return false; + if (!args.get(0).isNullOrUndefined()) { RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject()); diff --git a/js/src/builtin/WeakSetObject.h b/js/src/builtin/WeakSetObject.h index 308b67e67f..9aa4fc51d6 100644 --- a/js/src/builtin/WeakSetObject.h +++ b/js/src/builtin/WeakSetObject.h @@ -23,7 +23,7 @@ class WeakSetObject : public NativeObject static const JSPropertySpec properties[]; static const JSFunctionSpec methods[]; - static WeakSetObject* create(JSContext* cx); + static WeakSetObject* create(JSContext* cx, HandleObject proto = nullptr); static bool construct(JSContext* cx, unsigned argc, Value* vp); }; diff --git a/js/src/configure.in b/js/src/configure.in index 6fd49fc7a8..aecf3fa1a1 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -171,11 +171,12 @@ AC_SUBST(JS_SHARED_LIBRARY) if test "$JS_STANDALONE" = no; then autoconfmk=autoconf-js.mk JS_STANDALONE= + #DIST is exported from top-level configure else JS_STANDALONE=1 AC_DEFINE(JS_STANDALONE) + DIST="$MOZ_BUILD_ROOT/dist" fi -DIST="$MOZ_BUILD_ROOT/dist" AC_SUBST(JS_STANDALONE) BUILDING_JS=1 AC_SUBST(autoconfmk) @@ -377,6 +378,7 @@ case "$target" in AC_DEFINE(_CRT_SECURE_NO_WARNINGS) AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) + AC_DEFINE(_USE_MATH_DEFINES) # Otherwise MSVC's math.h doesn't #define M_PI. if test "$_CC_MAJOR_VERSION" = "18"; then _CC_SUITE=12 @@ -2144,10 +2146,10 @@ if test "$GNU_CC" -a "$OS_TARGET" != WINNT; then *) case $GCC_VERSION in 4.6*) - VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden_dso_handle.h' + VISIBILITY_FLAGS="-I${DIST}/system_wrappers -include ${_topsrcdir}/config/gcc_hidden_dso_handle.h" ;; *) - VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden.h' + VISIBILITY_FLAGS="-I${DIST}/system_wrappers -include ${_topsrcdir}/config/gcc_hidden.h" ;; esac WRAP_SYSTEM_INCLUDES=1 @@ -2164,13 +2166,13 @@ case "${OS_TARGET}" in WINNT|Darwin|Android) ;; *) - STL_FLAGS='-I$(DIST)/stl_wrappers' + STL_FLAGS="-I${DIST}/stl_wrappers" WRAP_STL_INCLUDES=1 ;; esac AC_SUBST(WRAP_SYSTEM_INCLUDES) -AC_SUBST(VISIBILITY_FLAGS) +AC_SUBST_LIST(VISIBILITY_FLAGS) dnl Checks for header files. dnl ======================================================== diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index fca000056d..192bab8a06 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -6977,7 +6977,12 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) ArgClosure argClosure(cif, result, args, static_cast(userData)); JSRuntime* rt = argClosure.cinfo->rt; RootedObject fun(rt, argClosure.cinfo->jsfnObj); - (void) js::PrepareScriptEnvironmentAndInvoke(rt, fun, argClosure); + + // Arbitrarily choose a cx in which to run this code. This is bad, as + // JSContexts are stateful and have options. The hope is to eliminate + // JSContexts (see bug 650361). + js::PrepareScriptEnvironmentAndInvoke(rt->contextList.getFirst(), fun, + argClosure); } bool CClosure::ArgClosure::operator()(JSContext* cx) @@ -7070,11 +7075,14 @@ bool CClosure::ArgClosure::operator()(JSContext* cx) size_t copySize = CType::GetSize(fninfo->mReturnType); MOZ_ASSERT(copySize <= rvSize); memcpy(result, cinfo->errResult, copySize); + + // We still want to return false here, so that + // PrepareScriptEnvironmentAndInvoke will report the exception. } else { // Bad case: not much we can do here. The rv is already zeroed out, so we // just return and hope for the best. - return false; } + return false; } // Small integer types must be returned as a word-sized ffi_arg. Coerce it diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 72887c7e5c..cd1b8ae844 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5374,9 +5374,9 @@ BytecodeEmitter::emitIterator() return false; if (!emit1(JSOP_SWAP)) // ITERFN OBJ return false; - if (!emitCall(JSOP_CALL, 0)) // ITER + if (!emitCall(JSOP_CALLITER, 0)) // ITER return false; - checkTypeSet(JSOP_CALL); + checkTypeSet(JSOP_CALLITER); return true; } diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index c0d2970204..9ba98ce36e 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -443,6 +443,13 @@ js::TraceRoot(JSTracer* trc, T* thingp, const char* name) DispatchToTracer(trc, ConvertToBase(thingp), name); } +template +void +js::TraceRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) +{ + TraceRoot(trc, thingp->unsafeGet(), name); +} + template void js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name) @@ -452,6 +459,13 @@ js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name) DispatchToTracer(trc, ConvertToBase(thingp), name); } +template +void +js::TraceNullableRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) +{ + TraceNullableRoot(trc, thingp->unsafeGet(), name); +} + template void js::TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase* vec, const char* name) @@ -484,7 +498,9 @@ js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name) template void js::TraceManuallyBarrieredEdge(JSTracer*, type*, const char*); \ template void js::TraceWeakEdge(JSTracer*, WeakRef*, const char*); \ template void js::TraceRoot(JSTracer*, type*, const char*); \ + template void js::TraceRoot(JSTracer*, ReadBarriered*, const char*); \ template void js::TraceNullableRoot(JSTracer*, type*, const char*); \ + template void js::TraceNullableRoot(JSTracer*, ReadBarriered*, const char*); \ template void js::TraceRange(JSTracer*, size_t, WriteBarrieredBase*, const char*); \ template void js::TraceRootRange(JSTracer*, size_t, type*, const char*); FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS) diff --git a/js/src/gc/Tracer.h b/js/src/gc/Tracer.h index 636133525d..33f0977dc8 100644 --- a/js/src/gc/Tracer.h +++ b/js/src/gc/Tracer.h @@ -63,12 +63,20 @@ template void TraceRoot(JSTracer* trc, T* thingp, const char* name); +template +void +TraceRoot(JSTracer* trc, ReadBarriered* thingp, const char* name); + // Idential to TraceRoot, except that this variant will not crash if |*thingp| // is null. template void TraceNullableRoot(JSTracer* trc, T* thingp, const char* name); +template +void +TraceNullableRoot(JSTracer* trc, ReadBarriered* thingp, const char* name); + // Like TraceEdge, but for edges that do not use one of the automatic barrier // classes and, thus, must be treated specially for moving GC. This method is // separate from TraceEdge to make accidental use of such edges more obvious. diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index ea3c14df6f..5519823ada 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -16,6 +16,7 @@ #include "ds/SplayTree.h" #include "gc/FindSCCs.h" #include "gc/GCRuntime.h" +#include "js/GCHashTable.h" #include "js/TracingAPI.h" #include "vm/MallocProvider.h" #include "vm/TypeInference.h" @@ -62,8 +63,16 @@ class ZoneHeapThreshold const AutoLockGC& lock); }; +struct UniqueIdGCPolicy { + static bool needsSweep(Cell** cell, uint64_t* value); +}; + // Maps a Cell* to a unique, 64bit id. -using UniqueIdMap = HashMap, SystemAllocPolicy>; +using UniqueIdMap = GCHashMap, + SystemAllocPolicy, + UniqueIdGCPolicy>; extern uint64_t NextCellUniqueId(JSRuntime* rt); diff --git a/js/src/jit-test/tests/TypedObject/bug976697.js b/js/src/jit-test/tests/TypedObject/bug976697.js deleted file mode 100644 index fa1a5de551..0000000000 --- a/js/src/jit-test/tests/TypedObject/bug976697.js +++ /dev/null @@ -1,15 +0,0 @@ -// Test that instantiating a typed array on top of a neutered buffer -// doesn't trip any asserts. -// -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -x = new ArrayBuffer(); -neuter(x, "same-data"); -new Uint32Array(x); -gc(); - -x = new ArrayBuffer(); -neuter(x, "change-data"); -new Uint32Array(x); -gc(); diff --git a/js/src/jit-test/tests/arguments/bug1227287.js b/js/src/jit-test/tests/arguments/bug1227287.js new file mode 100644 index 0000000000..7c43252f4a --- /dev/null +++ b/js/src/jit-test/tests/arguments/bug1227287.js @@ -0,0 +1,6 @@ +function f(y) { + y = 1; + assertEq(arguments.callee.arguments[0], 1); + return () => y; +} +f(0); diff --git a/js/src/jit-test/tests/baseline/arraySubclassPropertyLookup.js b/js/src/jit-test/tests/baseline/arraySubclassPropertyLookup.js new file mode 100644 index 0000000000..62b339f54f --- /dev/null +++ b/js/src/jit-test/tests/baseline/arraySubclassPropertyLookup.js @@ -0,0 +1,17 @@ +function f(v, expected) { + assertEq(v.prop, expected); +}; + +class SubArrayA extends Array { +} +class SubArrayB extends Array { +} +SubArrayA.prototype.prop = "A"; +SubArrayB.prototype.prop = "B"; + +var a = new SubArrayA(); +var b = new SubArrayB(); +for (let i = 0; i < 10; i++) { + f(a, "A"); + f(b, "B"); +} diff --git a/js/src/jit-test/tests/basic/iterable-error-messages.js b/js/src/jit-test/tests/basic/iterable-error-messages.js new file mode 100644 index 0000000000..4d10f92b22 --- /dev/null +++ b/js/src/jit-test/tests/basic/iterable-error-messages.js @@ -0,0 +1,39 @@ +function assertThrowsMsg(f, msg) { + try { + f(); + assertEq(0, 1); + } catch(e) { + assertEq(e instanceof TypeError, true); + assertEq(e.message, msg); + } +} + +// For-of +function testForOf(val) { + for (var x of val) {} +} +for (v of [{}, Math, new Proxy({}, {})]) { + assertThrowsMsg(() => testForOf(v), "val is not iterable"); +} +assertThrowsMsg(() => testForOf(null), "val is null"); +assertThrowsMsg(() => { for (var x of () => 1) {}}, "() => 1 is not iterable"); + +// Destructuring +function testDestr(val) { + var [a, b] = val; +} +for (v of [{}, Math, new Proxy({}, {})]) { + assertThrowsMsg(() => testDestr(v), "val is not iterable"); +} +assertThrowsMsg(() => testDestr(null), "val is null"); +assertThrowsMsg(() => { [a, b] = () => 1; }, "() => 1 is not iterable"); + +// Spread +function testSpread(val) { + [...val]; +} +for (v of [{}, Math, new Proxy({}, {})]) { + assertThrowsMsg(() => testSpread(v), "val is not iterable"); +} +assertThrowsMsg(() => testSpread(null), "val is null"); +assertThrowsMsg(() => { [...() => 1]; }, "() => 1 is not iterable"); diff --git a/js/src/jit-test/tests/ion/inline-Math-random-before-called.js b/js/src/jit-test/tests/ion/inline-Math-random-before-called.js new file mode 100644 index 0000000000..8db2fedfb2 --- /dev/null +++ b/js/src/jit-test/tests/ion/inline-Math-random-before-called.js @@ -0,0 +1,11 @@ +// |jit-test| ion-eager + +function ionCompiledEagerly() { + Math.random; // establish Math.random's identity for inlining + return function() { + return +Math.random(); // call will be inlined + }; +} + +var alreadyIonCompiled = ionCompiledEagerly(); +assertEq(alreadyIonCompiled() < 1, true); diff --git a/js/src/jit-test/tests/xdr/classes.js b/js/src/jit-test/tests/xdr/classes.js new file mode 100644 index 0000000000..e91da04eb0 --- /dev/null +++ b/js/src/jit-test/tests/xdr/classes.js @@ -0,0 +1,11 @@ +load(libdir + 'bytecode-cache.js'); +load(libdir + 'class.js'); + +if (!classesEnabled()) + quit(); + +var test = "new class extends class { } { constructor() { super(); } }()"; +evalWithCache(test, { assertEqBytecode : true }); + +var test = "new class { method() { super.toString(); } }().method()"; +evalWithCache(test, { assertEqBytecode : true }); diff --git a/js/src/jit/Bailouts.cpp b/js/src/jit/Bailouts.cpp index 958b4515f0..cf8f92446a 100644 --- a/js/src/jit/Bailouts.cpp +++ b/js/src/jit/Bailouts.cpp @@ -277,7 +277,7 @@ jit::CheckFrequentBailouts(JSContext* cx, JSScript* script, BailoutKind bailoutK // we compile this script LICM will be disabled. IonScript* ionScript = script->ionScript(); - if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold) { + if (ionScript->numBailouts() >= JitOptions.frequentBailoutThreshold) { // If we bailout because of the first execution of a basic block, // then we should record which basic block we are returning in, // which should prevent this from happening again. Also note that diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 686ec5f198..2efed8f696 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -800,7 +800,7 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC, // FirstExecution bailouts, we invalidate and recompile the script with // IonMonkey. Failing to increment the counter of the current basic block // might lead to repeated bailouts and invalidations. - if (!js_JitOptions.disablePgo && script->hasScriptCounts()) + if (!JitOptions.disablePgo && script->hasScriptCounts()) script->incHitCount(pc); JSOp op = JSOp(*pc); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index e2b2a36de8..bc31d36c8a 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -3098,6 +3098,12 @@ BaselineCompiler::emit_JSOP_CALL() return emitCall(); } +bool +BaselineCompiler::emit_JSOP_CALLITER() +{ + return emitCall(); +} + bool BaselineCompiler::emit_JSOP_NEW() { diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 069ff9468e..3edfa0af33 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -156,6 +156,7 @@ namespace jit { _(JSOP_INITALIASEDLEXICAL) \ _(JSOP_UNINITIALIZED) \ _(JSOP_CALL) \ + _(JSOP_CALLITER) \ _(JSOP_FUNCALL) \ _(JSOP_FUNAPPLY) \ _(JSOP_NEW) \ diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 37fd0d8057..7ed75abffd 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5690,7 +5690,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, if (native == StringConstructor) { RootedString emptyString(cx, cx->runtime()->emptyString); - res.set(StringObject::create(cx, emptyString, TenuredObject)); + res.set(StringObject::create(cx, emptyString, /* proto = */ nullptr, TenuredObject)); return !!res; } @@ -6153,10 +6153,16 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint res.set(vp[0]); } else { MOZ_ASSERT(op == JSOP_CALL || + op == JSOP_CALLITER || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY || op == JSOP_EVAL || op == JSOP_STRICTEVAL); + if (op == JSOP_CALLITER && callee.isPrimitive()) { + MOZ_ASSERT(argc == 0, "thisv must be on top of the stack"); + ReportValueError(cx, JSMSG_NOT_ITERABLE, -1, thisv, nullptr); + return false; + } if (!Invoke(cx, thisv, callee, argc, args, res)) return false; } diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 18bcaebd45..cc1b66bbba 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -309,7 +309,7 @@ CanEnterBaselineJIT(JSContext* cx, HandleScript script, InterpreterFrame* osrFra return Method_Compiled; // Check script warm-up counter. - if (script->incWarmUpCounter() <= js_JitOptions.baselineWarmUpThreshold) + if (script->incWarmUpCounter() <= JitOptions.baselineWarmUpThreshold) return Method_Skipped; // Frames can be marked as debuggee frames independently of its underlying diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 1a934c67c6..d268481659 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3612,7 +3612,7 @@ CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) Address length(ToRegister(apply->getElements()), ObjectElements::offsetOfLength()); masm.load32(length, tmp); - bailoutCmp32(Assembler::Above, tmp, Imm32(js_JitOptions.maxStackArgs), snapshot); + bailoutCmp32(Assembler::Above, tmp, Imm32(JitOptions.maxStackArgs), snapshot); Address initializedLength(ToRegister(apply->getElements()), ObjectElements::offsetOfInitializedLength()); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 4c6796e69a..534ad7fd45 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1514,7 +1514,7 @@ OptimizeMIR(MIRGenerator* mir) if (mir->shouldCancel("Start")) return false; - if (!js_JitOptions.disablePgo && !mir->compilingAsmJS()) { + if (!JitOptions.disablePgo && !mir->compilingAsmJS()) { AutoTraceLog log(logger, TraceLogger_PruneUnusedBranches); if (!PruneUnusedBranches(mir, graph)) return false; @@ -2338,7 +2338,7 @@ CheckScript(JSContext* cx, JSScript* script, bool osr) static MethodStatus CheckScriptSize(JSContext* cx, JSScript* script) { - if (!js_JitOptions.limitScriptSize) + if (!JitOptions.limitScriptSize) return Method_Compiled; uint32_t numLocalsAndArgs = NumLocalsAndArgs(script); @@ -2489,7 +2489,7 @@ jit::CanEnterAtBranch(JSContext* cx, HandleScript script, BaselineFrame* osrFram return Method_Skipped; // Optionally ignore on user request. - if (!js_JitOptions.osr) + if (!JitOptions.osr) return Method_Skipped; // Mark as forbidden if frame can't be handled. @@ -2508,7 +2508,7 @@ jit::CanEnterAtBranch(JSContext* cx, HandleScript script, BaselineFrame* osrFram bool force = false; if (script->hasIonScript() && pc != script->ionScript()->osrPc()) { uint32_t count = script->ionScript()->incrOsrPcMismatchCounter(); - if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile) + if (count <= JitOptions.osrPcMismatchesBeforeRecompile) return Method_Skipped; force = true; } @@ -2587,7 +2587,7 @@ jit::CanEnter(JSContext* cx, RunState& state) // If --ion-eager is used, compile with Baseline first, so that we // can directly enter IonMonkey. - if (js_JitOptions.eagerCompilation && !rscript->hasBaselineScript()) { + if (JitOptions.eagerCompilation && !rscript->hasBaselineScript()) { MethodStatus status = CanEnterBaselineMethod(cx, state); if (status != Method_Compiled) return status; @@ -2853,7 +2853,7 @@ InvalidateActivation(FreeOp* fop, const JitActivationIterator& activations, bool JitSpew(JitSpew_IonInvalidate, "BEGIN invalidating activation"); #ifdef CHECK_OSIPOINT_REGISTERS - if (js_JitOptions.checkOsiPointRegisters) + if (JitOptions.checkOsiPointRegisters) activations->asJit()->setCheckRegs(false); #endif diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index d26eca4db2..8a0b65ed1b 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -175,7 +175,7 @@ IsIonInlinablePC(jsbytecode* pc) { inline bool TooManyActualArguments(unsigned nargs) { - return nargs > js_JitOptions.maxStackArgs; + return nargs > JitOptions.maxStackArgs; } inline bool diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index db9e973d59..1bd5ad5dd2 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2449,7 +2449,7 @@ void jit::AssertGraphCoherency(MIRGraph& graph) { #ifdef DEBUG - if (!js_JitOptions.checkGraphConsistency) + if (!JitOptions.checkGraphConsistency) return; AssertBasicGraphCoherency(graph); AssertReversePostorder(graph); @@ -2536,7 +2536,7 @@ jit::AssertExtendedGraphCoherency(MIRGraph& graph) // are split) #ifdef DEBUG - if (!js_JitOptions.checkGraphConsistency) + if (!JitOptions.checkGraphConsistency) return; AssertGraphCoherency(graph); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 58d7e873ff..bf885f90f6 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1888,6 +1888,7 @@ IonBuilder::inspectOpcode(JSOp op) return jsop_funapply(GET_ARGC(pc)); case JSOP_CALL: + case JSOP_CALLITER: case JSOP_NEW: case JSOP_SUPERCALL: return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL); @@ -2444,7 +2445,7 @@ IonBuilder::restartLoop(CFGState state) { spew("New types at loop header, restarting loop body"); - if (js_JitOptions.limitScriptSize) { + if (JitOptions.limitScriptSize) { if (++numLoopRestarts_ >= MAX_LOOP_RESTARTS) return ControlStatus_Abort; } @@ -4805,7 +4806,7 @@ IonBuilder::arithTrySharedStub(bool* emitted, JSOp op, // Try to emit a shared stub cache. - if (js_JitOptions.disableSharedStubs) + if (JitOptions.disableSharedStubs) return true; // The actual jsop 'jsop_pos' is not supported yet. @@ -5286,7 +5287,7 @@ IonBuilder::makeInliningDecision(JSObject* targetArg, CallInfo& callInfo) // Cap the inlining depth. uint32_t maxInlineDepth; - if (js_JitOptions.isSmallFunction(targetScript)) { + if (JitOptions.isSmallFunction(targetScript)) { maxInlineDepth = optimizationInfo().smallFunctionMaxInlineDepth(); } else { maxInlineDepth = optimizationInfo().maxInlineDepth(); @@ -7041,7 +7042,7 @@ IonBuilder::compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDef // Try to emit a shared stub cache. - if (js_JitOptions.disableSharedStubs) + if (JitOptions.disableSharedStubs) return true; if (JSOp(*pc) == JSOP_CASE) @@ -10939,6 +10940,11 @@ IonBuilder::jsop_getprop(PropertyName* name) trackOptimizationAttempt(TrackedStrategy::GetProp_InlineAccess); if (!getPropTryInlineAccess(&emitted, obj, name, barrier, types) || emitted) return emitted; + + // Try to emit loads from a module namespace. + trackOptimizationAttempt(TrackedStrategy::GetProp_ModuleNamespace); + if (!getPropTryModuleNamespace(&emitted, obj, name, barrier, types) || emitted) + return emitted; } // Try to emit a polymorphic cache. @@ -11404,6 +11410,49 @@ IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName return true; } +bool +IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name, + BarrierKind barrier, TemporaryTypeSet* types) +{ + MOZ_ASSERT(*emitted == false); + + TemporaryTypeSet* objTypes = obj->resultTypeSet(); + if (!objTypes) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); + return true; + } + + JSObject* singleton = objTypes->maybeSingleton(); + if (!singleton) { + trackOptimizationOutcome(TrackedOutcome::NotSingleton); + return true; + } + + if (!singleton->is()) { + trackOptimizationOutcome(TrackedOutcome::NotModuleNamespace); + return true; + } + + ModuleNamespaceObject* ns = &singleton->as(); + ModuleEnvironmentObject* env; + Shape* shape; + if (!ns->bindings().lookup(NameToId(name), &env, &shape)) { + trackOptimizationOutcome(TrackedOutcome::UnknownProperty); + return true; + } + + obj->setImplicitlyUsedUnchecked(); + MConstant* envConst = constant(ObjectValue(*env)); + uint32_t slot = shape->slot(); + uint32_t nfixed = env->numFixedSlots(); + if (!loadSlot(envConst, slot, nfixed, types->getKnownMIRType(), barrier, types)) + return false; + + trackOptimizationSuccess(); + *emitted = true; + return true; +} + MInstruction* IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, BarrierKind barrier, TemporaryTypeSet* types) @@ -11908,7 +11957,7 @@ IonBuilder::getPropTrySharedStub(bool* emitted, MDefinition* obj) // Try to emit a shared stub cache. - if (js_JitOptions.disableSharedStubs) + if (JitOptions.disableSharedStubs) return true; MInstruction* stub = MUnarySharedStub::New(alloc(), obj); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index aa01631683..6d466d27bf 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -433,6 +433,8 @@ class IonBuilder bool getPropTryConstant(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types); bool getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); + bool getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name, + BarrierKind barrier, TemporaryTypeSet* types); bool getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); bool getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name, @@ -1266,7 +1268,7 @@ class IonBuilder } bool forceInlineCaches() { - return MOZ_UNLIKELY(js_JitOptions.forceInlineCaches); + return MOZ_UNLIKELY(JitOptions.forceInlineCaches); } // Out-of-line variants that don't check if optimization tracking is diff --git a/js/src/jit/IonOptimizationLevels.cpp b/js/src/jit/IonOptimizationLevels.cpp index 98efc8feb5..5143c1411a 100644 --- a/js/src/jit/IonOptimizationLevels.cpp +++ b/js/src/jit/IonOptimizationLevels.cpp @@ -82,8 +82,8 @@ OptimizationInfo::compilerWarmUpThreshold(JSScript* script, jsbytecode* pc) cons pc = nullptr; uint32_t warmUpThreshold = compilerWarmUpThreshold_; - if (js_JitOptions.forcedDefaultIonWarmUpThreshold.isSome()) - warmUpThreshold = js_JitOptions.forcedDefaultIonWarmUpThreshold.ref(); + if (JitOptions.forcedDefaultIonWarmUpThreshold.isSome()) + warmUpThreshold = JitOptions.forcedDefaultIonWarmUpThreshold.ref(); // If the script is too large to compile on the main thread, we can still // compile it off thread. In these cases, increase the warm-up counter @@ -97,7 +97,7 @@ OptimizationInfo::compilerWarmUpThreshold(JSScript* script, jsbytecode* pc) cons if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS) warmUpThreshold *= (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS); - if (!pc || js_JitOptions.eagerCompilation) + if (!pc || JitOptions.eagerCompilation) return warmUpThreshold; // It's more efficient to enter outer loops, rather than inner loops, via OSR. diff --git a/js/src/jit/IonOptimizationLevels.h b/js/src/jit/IonOptimizationLevels.h index f5339d3a81..0ca81822a9 100644 --- a/js/src/jit/IonOptimizationLevels.h +++ b/js/src/jit/IonOptimizationLevels.h @@ -152,37 +152,37 @@ class OptimizationInfo } bool inlineInterpreted() const { - return inlineInterpreted_ && !js_JitOptions.disableInlining; + return inlineInterpreted_ && !JitOptions.disableInlining; } bool inlineNative() const { - return inlineNative_ && !js_JitOptions.disableInlining; + return inlineNative_ && !JitOptions.disableInlining; } uint32_t compilerWarmUpThreshold(JSScript* script, jsbytecode* pc = nullptr) const; bool eagerSimdUnboxEnabled() const { - return eagerSimdUnbox_ && !js_JitOptions.disableEagerSimdUnbox; + return eagerSimdUnbox_ && !JitOptions.disableEagerSimdUnbox; } bool gvnEnabled() const { - return gvn_ && !js_JitOptions.disableGvn; + return gvn_ && !JitOptions.disableGvn; } bool licmEnabled() const { - return licm_ && !js_JitOptions.disableLicm; + return licm_ && !JitOptions.disableLicm; } bool rangeAnalysisEnabled() const { - return rangeAnalysis_ && !js_JitOptions.disableRangeAnalysis; + return rangeAnalysis_ && !JitOptions.disableRangeAnalysis; } bool loopUnrollingEnabled() const { - return loopUnrolling_ && !js_JitOptions.disableLoopUnrolling; + return loopUnrolling_ && !JitOptions.disableLoopUnrolling; } bool instructionReorderingEnabled() const { - return reordering_ && !js_JitOptions.disableInstructionReordering; + return reordering_ && !JitOptions.disableInstructionReordering; } bool autoTruncateEnabled() const { @@ -190,23 +190,23 @@ class OptimizationInfo } bool sincosEnabled() const { - return sincos_ && !js_JitOptions.disableSincos; + return sincos_ && !JitOptions.disableSincos; } bool sinkEnabled() const { - return sink_ && !js_JitOptions.disableSink; + return sink_ && !JitOptions.disableSink; } bool eaaEnabled() const { - return eaa_ && !js_JitOptions.disableEaa; + return eaa_ && !JitOptions.disableEaa; } bool amaEnabled() const { - return ama_ && !js_JitOptions.disableAma; + return ama_ && !JitOptions.disableAma; } bool edgeCaseAnalysisEnabled() const { - return edgeCaseAnalysis_ && !js_JitOptions.disableEdgeCaseAnalysis; + return edgeCaseAnalysis_ && !JitOptions.disableEdgeCaseAnalysis; } bool eliminateRedundantChecksEnabled() const { @@ -214,13 +214,13 @@ class OptimizationInfo } IonRegisterAllocator registerAllocator() const { - if (js_JitOptions.forcedRegisterAllocator.isSome()) - return js_JitOptions.forcedRegisterAllocator.ref(); + if (JitOptions.forcedRegisterAllocator.isSome()) + return JitOptions.forcedRegisterAllocator.ref(); return registerAllocator_; } bool scalarReplacementEnabled() const { - return scalarReplacement_ && !js_JitOptions.disableScalarReplacement; + return scalarReplacement_ && !JitOptions.disableScalarReplacement; } uint32_t smallFunctionMaxInlineDepth() const { @@ -234,7 +234,7 @@ class OptimizationInfo } uint32_t inlineMaxBytecodePerCallSite(bool offThread) const { - return (offThread || !js_JitOptions.limitScriptSize) + return (offThread || !JitOptions.limitScriptSize) ? inlineMaxBytecodePerCallSiteOffThread_ : inlineMaxBytecodePerCallSiteMainThread_; } @@ -253,8 +253,8 @@ class OptimizationInfo uint32_t inliningWarmUpThreshold() const { uint32_t compilerWarmUpThreshold = compilerWarmUpThreshold_; - if (js_JitOptions.forcedDefaultIonWarmUpThreshold.isSome()) - compilerWarmUpThreshold = js_JitOptions.forcedDefaultIonWarmUpThreshold.ref(); + if (JitOptions.forcedDefaultIonWarmUpThreshold.isSome()) + compilerWarmUpThreshold = JitOptions.forcedDefaultIonWarmUpThreshold.ref(); return compilerWarmUpThreshold * inliningWarmUpThresholdFactor_; } diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 8db50802f2..76d8de1aae 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -827,7 +827,7 @@ HandleException(ResumeFromException* rfe) JitActivation* activation = cx->runtime()->activation()->asJit(); #ifdef CHECK_OSIPOINT_REGISTERS - if (js_JitOptions.checkOsiPointRegisters) + if (JitOptions.checkOsiPointRegisters) activation->setCheckRegs(false); #endif @@ -1530,7 +1530,7 @@ MarkJitActivation(JSTracer* trc, const JitActivationIterator& activations) JitActivation* activation = activations->asJit(); #ifdef CHECK_OSIPOINT_REGISTERS - if (js_JitOptions.checkOsiPointRegisters) { + if (JitOptions.checkOsiPointRegisters) { // GC can modify spilled registers, breaking our register checks. // To handle this, we disable these checks for the current VM call // when a GC happens. diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index 559e73d8f2..8d43464ad5 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -17,7 +17,7 @@ using mozilla::Maybe; namespace js { namespace jit { -JitOptions js_JitOptions; +DefaultJitOptions JitOptions; static void Warn(const char* env, const char* value) { @@ -57,7 +57,7 @@ T overrideDefault(const char* param, T dflt) { return dflt; } #define SET_DEFAULT(var, dflt) var = overrideDefault("JIT_OPTION_" #var, dflt) -JitOptions::JitOptions() +DefaultJitOptions::DefaultJitOptions() { // Whether to perform expensive graph-consistency DEBUG-only assertions. // It can be useful to disable this to reduce DEBUG-compile time of large @@ -187,19 +187,19 @@ JitOptions::JitOptions() } bool -JitOptions::isSmallFunction(JSScript* script) const +DefaultJitOptions::isSmallFunction(JSScript* script) const { return script->length() <= smallFunctionMaxBytecodeLength_; } void -JitOptions::enableGvn(bool enable) +DefaultJitOptions::enableGvn(bool enable) { disableGvn = !enable; } void -JitOptions::setEagerCompilation() +DefaultJitOptions::setEagerCompilation() { eagerCompilation = true; baselineWarmUpThreshold = 0; @@ -208,27 +208,27 @@ JitOptions::setEagerCompilation() } void -JitOptions::setCompilerWarmUpThreshold(uint32_t warmUpThreshold) +DefaultJitOptions::setCompilerWarmUpThreshold(uint32_t warmUpThreshold) { forcedDefaultIonWarmUpThreshold.reset(); forcedDefaultIonWarmUpThreshold.emplace(warmUpThreshold); // Undo eager compilation if (eagerCompilation && warmUpThreshold != 0) { - jit::JitOptions defaultValues; + jit::DefaultJitOptions defaultValues; eagerCompilation = false; baselineWarmUpThreshold = defaultValues.baselineWarmUpThreshold; } } void -JitOptions::resetCompilerWarmUpThreshold() +DefaultJitOptions::resetCompilerWarmUpThreshold() { forcedDefaultIonWarmUpThreshold.reset(); // Undo eager compilation if (eagerCompilation) { - jit::JitOptions defaultValues; + jit::DefaultJitOptions defaultValues; eagerCompilation = false; baselineWarmUpThreshold = defaultValues.baselineWarmUpThreshold; } diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 0402bb5286..68a10b5a81 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -39,7 +39,7 @@ LookupRegisterAllocator(const char* name) return mozilla::Nothing(); } -struct JitOptions +struct DefaultJitOptions { bool checkGraphConsistency; #ifdef CHECK_OSIPOINT_REGISTERS @@ -78,7 +78,7 @@ struct JitOptions // The options below affect the rest of the VM, and not just the JIT. bool disableUnboxedObjects; - JitOptions(); + DefaultJitOptions(); bool isSmallFunction(JSScript* script) const; void setEagerCompilation(); void setCompilerWarmUpThreshold(uint32_t warmUpThreshold); @@ -86,7 +86,7 @@ struct JitOptions void enableGvn(bool val); }; -extern JitOptions js_JitOptions; +extern DefaultJitOptions JitOptions; } // namespace jit } // namespace js diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 70f9c41b01..a894ace254 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -588,7 +588,7 @@ LIRGenerator::visitAssertFloat32(MAssertFloat32* assertion) MIRType type = assertion->input()->type(); DebugOnly checkIsFloat32 = assertion->mustBeFloat32(); - if (type != MIRType_Value && !js_JitOptions.eagerCompilation) { + if (type != MIRType_Value && !JitOptions.eagerCompilation) { MOZ_ASSERT_IF(checkIsFloat32, type == MIRType_Float32); MOZ_ASSERT_IF(!checkIsFloat32, type != MIRType_Float32); } diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index ae4c8ebdbc..c6e04000ed 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -1349,8 +1349,10 @@ IonBuilder::inlineMathRandom(CallInfo& callInfo) if (getInlineReturnType() != MIRType_Double) return InliningStatus_NotInlined; - MOZ_ASSERT(script()->compartment()->randomNumberGenerator.isSome(), - "MRandom JIT code depends on RNG being initialized"); + // MRandom JIT code directly accesses the RNG. It's (barely) possible to + // inline Math.random without it having been called yet, so ensure RNG + // state that isn't guaranteed to be initialized already. + script()->compartment()->ensureRandomNumberGenerator(); callInfo.setImplicitlyUsedUnchecked(); @@ -2561,7 +2563,7 @@ IonBuilder::inlineAssertRecoveredOnBailout(CallInfo& callInfo) if (callInfo.argc() != 2) return InliningStatus_NotInlined; - if (js_JitOptions.checkRangeAnalysis) { + if (JitOptions.checkRangeAnalysis) { // If we are checking the range of all instructions, then the guards // inserted by Range Analysis prevent the use of recover // instruction. Thus, we just disable these checks. diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 969ea6534b..cca96c1232 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -1754,9 +1754,9 @@ MArgumentsLength::computeRange(TempAllocator& alloc) { // This is is a conservative upper bound on what |TooManyActualArguments| // checks. If exceeded, Ion will not be entered in the first place. - MOZ_ASSERT(js_JitOptions.maxStackArgs <= UINT32_MAX, + MOZ_ASSERT(JitOptions.maxStackArgs <= UINT32_MAX, "NewUInt32Range requires a uint32 value"); - setRange(Range::NewUInt32Range(alloc, 0, js_JitOptions.maxStackArgs)); + setRange(Range::NewUInt32Range(alloc, 0, JitOptions.maxStackArgs)); } void @@ -2256,7 +2256,7 @@ RangeAnalysis::analyze() bool RangeAnalysis::addRangeAssertions() { - if (!js_JitOptions.checkRangeAnalysis) + if (!JitOptions.checkRangeAnalysis) return true; // Check the computed range for this instruction, if the option is set. Note diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 5988bcda49..d2534134c7 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -70,7 +70,7 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator* gen, LIRGraph* graph, Mac osrEntryOffset_(0), skipArgCheckEntryOffset_(0), #ifdef CHECK_OSIPOINT_REGISTERS - checkOsiPointRegisters(js_JitOptions.checkOsiPointRegisters), + checkOsiPointRegisters(JitOptions.checkOsiPointRegisters), #endif frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()), frameInitialAdjustment_(0) diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index 23218403cf..f534c58d25 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -176,7 +176,7 @@ class CodeGeneratorShared : public LElementVisitor protected: #ifdef CHECK_OSIPOINT_REGISTERS - // See js_JitOptions.checkOsiPointRegisters. We set this here to avoid + // See JitOptions.checkOsiPointRegisters. We set this here to avoid // races when enableOsiPointRegisterChecks is called while we're generating // code off-thread. bool checkOsiPointRegisters; diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 42f69b8d1f..c1fe853a91 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -284,7 +284,7 @@ LIRGeneratorShared::redefine(MDefinition* def, MDefinition* as) def->setVirtualRegister(as->virtualRegister()); #ifdef DEBUG - if (js_JitOptions.runExtraChecks && + if (JitOptions.runExtraChecks && def->resultTypeSet() && as->resultTypeSet() && !def->resultTypeSet()->equals(as->resultTypeSet())) { diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 577cb65f21..d6a1eb294d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5859,35 +5859,35 @@ JS_SetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt, uint32_t v switch (opt) { case JSJITCOMPILER_BASELINE_WARMUP_TRIGGER: if (value == uint32_t(-1)) { - jit::JitOptions defaultValues; + jit::DefaultJitOptions defaultValues; value = defaultValues.baselineWarmUpThreshold; } - jit::js_JitOptions.baselineWarmUpThreshold = value; + jit::JitOptions.baselineWarmUpThreshold = value; break; case JSJITCOMPILER_ION_WARMUP_TRIGGER: if (value == uint32_t(-1)) { - jit::js_JitOptions.resetCompilerWarmUpThreshold(); + jit::JitOptions.resetCompilerWarmUpThreshold(); break; } - jit::js_JitOptions.setCompilerWarmUpThreshold(value); + jit::JitOptions.setCompilerWarmUpThreshold(value); if (value == 0) - jit::js_JitOptions.setEagerCompilation(); + jit::JitOptions.setEagerCompilation(); break; case JSJITCOMPILER_ION_GVN_ENABLE: if (value == 0) { - jit::js_JitOptions.enableGvn(false); + jit::JitOptions.enableGvn(false); JitSpew(js::jit::JitSpew_IonScripts, "Disable ion's GVN"); } else { - jit::js_JitOptions.enableGvn(true); + jit::JitOptions.enableGvn(true); JitSpew(js::jit::JitSpew_IonScripts, "Enable ion's GVN"); } break; case JSJITCOMPILER_ION_FORCE_IC: if (value == 0) { - jit::js_JitOptions.forceInlineCaches = false; + jit::JitOptions.forceInlineCaches = false; JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Enable non-IC optimizations."); } else { - jit::js_JitOptions.forceInlineCaches = true; + jit::JitOptions.forceInlineCaches = true; JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Disable non-IC optimizations."); } break; @@ -5940,13 +5940,13 @@ JS_GetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt) #ifndef JS_CODEGEN_NONE switch (opt) { case JSJITCOMPILER_BASELINE_WARMUP_TRIGGER: - return jit::js_JitOptions.baselineWarmUpThreshold; + return jit::JitOptions.baselineWarmUpThreshold; case JSJITCOMPILER_ION_WARMUP_TRIGGER: - return jit::js_JitOptions.forcedDefaultIonWarmUpThreshold.isSome() - ? jit::js_JitOptions.forcedDefaultIonWarmUpThreshold.ref() + return jit::JitOptions.forcedDefaultIonWarmUpThreshold.isSome() + ? jit::JitOptions.forcedDefaultIonWarmUpThreshold.ref() : jit::OptimizationInfo::CompilerWarmupThreshold; case JSJITCOMPILER_ION_FORCE_IC: - return jit::js_JitOptions.forceInlineCaches; + return jit::JitOptions.forceInlineCaches; case JSJITCOMPILER_ION_ENABLE: return JS::RuntimeOptionsRef(rt).ion(); case JSJITCOMPILER_BASELINE_ENABLE: diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index b0900f588d..926a7a01e3 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1653,7 +1653,7 @@ MatchNumericComparator(JSContext* cx, const Value& v) return Match_None; JSFunction* fun = &obj.as(); - if (!fun->isInterpreted()) + if (!fun->isInterpreted() || fun->isClassConstructor()) return Match_None; JSScript* script = fun->getOrCreateScript(cx); @@ -3066,9 +3066,9 @@ IsArrayConstructor(const Value& v) } static bool -ArrayFromCallArgs(JSContext* cx, CallArgs& args) +ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr) { - JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length()); + JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto); if (!obj) return false; @@ -3189,8 +3189,12 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + if (args.length() != 1 || !args[0].isNumber()) - return ArrayFromCallArgs(cx, args); + return ArrayFromCallArgs(cx, args, proto); uint32_t length; if (args[0].isInt32()) { @@ -3209,7 +3213,7 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) } } - JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length); + JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto); if (!obj) return false; @@ -3344,12 +3348,6 @@ EnsureNewArrayElements(ExclusiveContext* cx, ArrayObject* obj, uint32_t length) return true; } -static bool -NewArrayIsCachable(ExclusiveContext* cxArg, NewObjectKind newKind) -{ - return cxArg->isJSContext() && newKind == GenericObject; -} - template static MOZ_ALWAYS_INLINE ArrayObject* NewArray(ExclusiveContext* cxArg, uint32_t length, @@ -3359,13 +3357,18 @@ NewArray(ExclusiveContext* cxArg, uint32_t length, MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_)); allocKind = GetBackgroundAllocKind(allocKind); - bool isCachable = NewArrayIsCachable(cxArg, newKind); + RootedObject proto(cxArg, protoArg); + if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) + return nullptr; + + Rooted taggedProto(cxArg, TaggedProto(proto)); + bool isCachable = NewObjectWithTaggedProtoIsCachable(cxArg, taggedProto, newKind, &ArrayObject::class_); if (isCachable) { JSContext* cx = cxArg->asJSContext(); JSRuntime* rt = cx->runtime(); NewObjectCache& cache = rt->newObjectCache; NewObjectCache::EntryIndex entry = -1; - if (cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry)) { + if (cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry)) { gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_); AutoSetNewObjectMetadata metadata(cx); JSObject* obj = cache.newObjectFromHit(cx, entry, heap); @@ -3384,10 +3387,6 @@ NewArray(ExclusiveContext* cxArg, uint32_t length, } } - RootedObject proto(cxArg, protoArg); - if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) - return nullptr; - RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, &ArrayObject::class_, TaggedProto(proto))); if (!group) @@ -3423,8 +3422,8 @@ NewArray(ExclusiveContext* cxArg, uint32_t length, if (isCachable) { NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache; NewObjectCache::EntryIndex entry = -1; - cache.lookupGlobal(&ArrayObject::class_, cxArg->global(), allocKind, &entry); - cache.fillGlobal(entry, &ArrayObject::class_, cxArg->global(), allocKind, arr); + cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry); + cache.fillProto(entry, &ArrayObject::class_, taggedProto, allocKind, arr); } if (maxLength > 0 && !EnsureNewArrayElements(cxArg, arr, std::min(maxLength, length))) @@ -3524,7 +3523,9 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc } // Return a new boxed or unboxed array with the specified length and allocated -// capacity (up to maxLength), using the specified group if possible. +// capacity (up to maxLength), using the specified group if possible. If the +// specified group cannot be used, ensure that the created array at least has +// the given [[Prototype]]. template static inline JSObject* NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, @@ -3538,14 +3539,14 @@ NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length if (group->shouldPreTenure() || group->maybePreliminaryObjects()) newKind = TenuredObject; + RootedObject proto(cx, group->proto().toObject()); if (group->maybeUnboxedLayout()) { if (length > UnboxedArrayObject::MaximumCapacity) - return NewArray(cx, length, nullptr, newKind); - + return NewArray(cx, length, proto, newKind); return UnboxedArrayObject::create(cx, group, length, newKind, maxLength); } - ArrayObject* res = NewArray(cx, length, nullptr, newKind); + ArrayObject* res = NewArray(cx, length, proto, newKind); if (!res) return nullptr; @@ -3624,9 +3625,9 @@ js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, } JSObject* -js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length) +js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto) { - RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto)); if (!group) return nullptr; return NewArrayTryUseGroup(cx, group, length); @@ -3686,9 +3687,10 @@ js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, } JSObject* -js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length) +js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, + HandleObject proto /* = nullptr */) { - RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto)); if (!group) return nullptr; return NewCopiedArrayTryUseGroup(cx, group, vp, length); diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 957c32ffaa..160be920b6 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -101,7 +101,7 @@ NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, bool forceAnalyze = false); extern JSObject* -NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length); +NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto); enum class ShouldUpdateTypes { @@ -116,7 +116,8 @@ NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); extern JSObject* -NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length); +NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, + HandleObject proto = nullptr); /* * Determines whether a write to the given element on |obj| should fail because diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 1341a24a38..3d3c0e4d82 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -245,20 +245,8 @@ js::MarkWellKnownSymbols(JSTracer* trc) void JSRuntime::sweepAtoms() { - if (!atoms_) - return; - - for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) { - AtomStateEntry entry = e.front(); - JSAtom* atom = entry.asPtrUnbarriered(); - bool isDying = IsAboutToBeFinalizedUnbarriered(&atom); - - /* Pinned or interned key cannot be finalized. */ - MOZ_ASSERT_IF(hasContexts() && entry.isPinned(), !isDying); - - if (isDying) - e.removeFront(); - } + if (atoms_) + atoms_->sweep(); } bool diff --git a/js/src/jsatom.h b/js/src/jsatom.h index da57455bd8..ed1a6c9adf 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -12,8 +12,10 @@ #include "jsalloc.h" #include "gc/Barrier.h" +#include "gc/Marking.h" #include "gc/Rooting.h" #include "js/GCAPI.h" +#include "js/GCHashTable.h" #include "vm/CommonPropertyNames.h" class JSAtom; @@ -76,6 +78,11 @@ class AtomStateEntry JSAtom* asPtr() const; JSAtom* asPtrUnbarriered() const; + + bool needsSweep() { + JSAtom* atom = asPtrUnbarriered(); + return gc::IsAboutToBeFinalizedUnbarriered(&atom); + } }; struct AtomHasher @@ -111,7 +118,7 @@ struct AtomHasher static void rekey(AtomStateEntry& k, const AtomStateEntry& newKey) { k = newKey; } }; -typedef HashSet AtomSet; +using AtomSet = js::GCHashSet; // This class is a wrapper for AtomSet that is used to ensure the AtomSet is // not modified. It should only expose read-only methods from AtomSet. diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index b5cd8ad404..c8109b02c2 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -116,7 +116,13 @@ Boolean(JSContext* cx, unsigned argc, Value* vp) bool b = args.length() != 0 ? JS::ToBoolean(args[0]) : false; if (args.isConstructing()) { - JSObject* obj = BooleanObject::create(cx, b); + RootedObject newTarget (cx, &args.newTarget().toObject()); + RootedObject proto(cx); + + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + JSObject* obj = BooleanObject::create(cx, b, proto); if (!obj) return false; args.rval().setObject(*obj); diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index e4a9534041..9ef1f8aad5 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -31,6 +31,7 @@ #include "jsfuninlines.h" #include "jsgcinlines.h" #include "jsobjinlines.h" +#include "jsscriptinlines.h" using namespace js; using namespace js::gc; @@ -655,13 +656,13 @@ JSCompartment::sweepAfterMinorGC() globalWriteBarriered = false; if (innerViews.needsSweepAfterMinorGC()) - innerViews.sweepAfterMinorGC(runtimeFromMainThread()); + innerViews.sweepAfterMinorGC(); } void JSCompartment::sweepInnerViews() { - innerViews.sweep(runtimeFromAnyThread()); + innerViews.sweep(); } void @@ -1067,7 +1068,7 @@ JSCompartment::updateDebuggerObservesCoverage() bool JSCompartment::collectCoverage() const { - return !js_JitOptions.disablePgo || + return !JitOptions.disablePgo || debuggerObservesCoverage() || runtimeFromAnyThread()->profilingScripts || runtimeFromAnyThread()->lcovOutput.isEnabled(); diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index cae65370ab..97e698e5f6 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -3000,7 +3000,14 @@ static const JSFunctionSpec date_methods[] = { static bool NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t) { - JSObject* obj = NewDateObjectMsec(cx, t); + MOZ_ASSERT(args.isConstructing()); + + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + JSObject* obj = NewDateObjectMsec(cx, t, proto); if (!obj) return false; @@ -3229,9 +3236,9 @@ const Class DateObject::protoClass_ = { }; JSObject* -js::NewDateObjectMsec(JSContext* cx, ClippedTime t) +js::NewDateObjectMsec(JSContext* cx, ClippedTime t, HandleObject proto /* = nullptr */) { - JSObject* obj = NewBuiltinClassInstance(cx, &DateObject::class_); + JSObject* obj = NewObjectWithClassProto(cx, &DateObject::class_, proto); if (!obj) return nullptr; obj->as().setUTCTime(t); diff --git a/js/src/jsdate.h b/js/src/jsdate.h index 29bde83bb2..7dc62b4711 100644 --- a/js/src/jsdate.h +++ b/js/src/jsdate.h @@ -30,7 +30,7 @@ namespace js { * since the epoch. */ extern JSObject* -NewDateObjectMsec(JSContext* cx, JS::ClippedTime t); +NewDateObjectMsec(JSContext* cx, JS::ClippedTime t, JS::HandleObject proto = nullptr); /* * Construct a new Date Object from an exploded local time value. diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 2bba65d7a4..d9fc518f58 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -338,6 +338,11 @@ Error(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); + // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + /* Compute the error message, if any. */ RootedString message(cx, nullptr); if (args.hasDefined(0)) { @@ -389,7 +394,7 @@ Error(JSContext* cx, unsigned argc, Value* vp) JSExnType exnType = JSExnType(args.callee().as().getExtendedSlot(0).toInt32()); RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName, - lineNumber, columnNumber, nullptr, message)); + lineNumber, columnNumber, nullptr, message, proto)); if (!obj) return false; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 9731775da9..d249a3daa9 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1139,24 +1139,28 @@ js::detail::IdMatchesAtom(jsid id, JSAtom* atom) return id == INTERNED_STRING_TO_JSID(nullptr, atom); } -JS_FRIEND_API(bool) -js::PrepareScriptEnvironmentAndInvoke(JSRuntime* rt, HandleObject scope, ScriptEnvironmentPreparer::Closure& closure) +JS_FRIEND_API(void) +js::PrepareScriptEnvironmentAndInvoke(JSContext* cx, HandleObject scope, ScriptEnvironmentPreparer::Closure& closure) { - if (rt->scriptEnvironmentPreparer) - return rt->scriptEnvironmentPreparer->invoke(scope, closure); + MOZ_ASSERT(!cx->isExceptionPending()); + + if (cx->runtime()->scriptEnvironmentPreparer) { + cx->runtime()->scriptEnvironmentPreparer->invoke(scope, closure); + return; + } - MOZ_ASSERT(rt->contextList.getFirst() == rt->contextList.getLast()); - JSContext* cx = rt->contextList.getFirst(); JSAutoCompartment ac(cx, scope); bool ok = closure(cx); + MOZ_ASSERT_IF(ok, !cx->isExceptionPending()); + // NB: This does not affect Gecko, which has a prepareScriptEnvironment // callback. - if (JS_IsExceptionPending(cx)) { + if (!ok) { JS_ReportPendingException(cx); } - return ok; + MOZ_ASSERT(!cx->isExceptionPending()); } JS_FRIEND_API(void) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 966ce7ea8e..8267b15b15 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -882,6 +882,27 @@ CopyFlatStringChars(char16_t* dest, JSFlatString* s, size_t len) CopyLinearStringChars(dest, FlatStringToLinearString(s), len); } +/** + * Add some or all property keys of obj to the id vector *props. + * + * The flags parameter controls which property keys are added. Pass a + * combination of the following bits: + * + * JSITER_OWNONLY - Don't also search the prototype chain; only consider + * obj's own properties. + * + * JSITER_HIDDEN - Include nonenumerable properties. + * + * JSITER_SYMBOLS - Include property keys that are symbols. The default + * behavior is to filter out symbols. + * + * JSITER_SYMBOLSONLY - Exclude non-symbol property keys. + * + * This is the closest C++ API we have to `Reflect.ownKeys(obj)`, or + * equivalently, the ES6 [[OwnPropertyKeys]] internal method. Pass + * `JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS` as flags to get + * results that match the output of Reflect.ownKeys. + */ JS_FRIEND_API(bool) GetPropertyKeys(JSContext* cx, JS::HandleObject obj, unsigned flags, JS::AutoIdVector* props); @@ -2524,11 +2545,16 @@ IdToValue(jsid id) * PrepareScriptEnvironmentAndInvoke will call the preparer's 'invoke' method * with the given |closure|, with the assumption that the preparer will set up * any state necessary to run script in |scope|, invoke |closure| with a valid - * JSContext*, and return. + * JSContext*, report any exceptions thrown from the closure, and return. * * If no preparer is registered, PrepareScriptEnvironmentAndInvoke will assert * that |rt| has exactly one JSContext associated with it, enter the compartment * of |scope| on that context, and invoke |closure|. + * + * In both cases, PrepareScriptEnvironmentAndInvoke will report any exceptions + * that are thrown by the closure. Consumers who want to propagate back + * whether the closure succeeded should do so via members of the closure + * itself. */ struct ScriptEnvironmentPreparer { @@ -2536,11 +2562,11 @@ struct ScriptEnvironmentPreparer { virtual bool operator()(JSContext* cx) = 0; }; - virtual bool invoke(JS::HandleObject scope, Closure& closure) = 0; + virtual void invoke(JS::HandleObject scope, Closure& closure) = 0; }; -extern JS_FRIEND_API(bool) -PrepareScriptEnvironmentAndInvoke(JSRuntime* rt, JS::HandleObject scope, +extern JS_FRIEND_API(void) +PrepareScriptEnvironmentAndInvoke(JSContext* cx, JS::HandleObject scope, ScriptEnvironmentPreparer::Closure& closure); JS_FRIEND_API(void) @@ -2767,9 +2793,9 @@ JS_FRIEND_API(bool) IsWindowProxy(JSObject* obj); /** - * If `obj` is a Window, get its associated WindowProxy (or a CCW if the - * page was navigated away from), else return `obj`. This function is - * infallible and never returns nullptr. + * If `obj` is a Window, get its associated WindowProxy (or a CCW or dead + * wrapper if the page was navigated away from), else return `obj`. This + * function is infallible and never returns nullptr. */ extern JS_FRIEND_API(JSObject*) ToWindowProxyIfWindow(JSObject* obj); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index b912a5ac26..57ed25db70 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1693,6 +1693,22 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp) return true; } +static JSFunction* +NewNativeFunctionWithGivenProto(JSContext* cx, Native native, unsigned nargs, + HandleAtom atom, HandleObject proto) +{ + return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_FUN, nullptr, atom, proto, + AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto); +} + +static JSFunction* +NewNativeConstructorWithGivenProto(JSContext* cx, Native native, unsigned nargs, + HandleAtom atom, HandleObject proto) +{ + return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_CTOR, nullptr, atom, proto, + AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto); +} + // ES6 draft rev32 19.2.3.2 bool js::fun_bind(JSContext* cx, unsigned argc, Value* vp) @@ -1719,6 +1735,11 @@ js::fun_bind(JSContext* cx, unsigned argc, Value* vp) RootedValue thisArg(cx, args.length() >= 1 ? args[0] : UndefinedValue()); RootedObject target(cx, &thisv.toObject()); + // This is part of step 4, but we're delaying allocating the function object. + RootedObject proto(cx); + if (!GetPrototype(cx, target, &proto)) + return false; + double length = 0.0; // Try to avoid invoking the resolve hook. if (target->is() && !target->as().hasResolvedLength()) { @@ -1774,8 +1795,8 @@ js::fun_bind(JSContext* cx, unsigned argc, Value* vp) // Step 4. RootedFunction fun(cx, target->isConstructor() ? - NewNativeConstructor(cx, CallOrConstructBoundFunction, length, nameAtom) : - NewNativeFunction(cx, CallOrConstructBoundFunction, length, nameAtom)); + NewNativeConstructorWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto) : + NewNativeFunctionWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto)); if (!fun) return false; @@ -1942,7 +1963,11 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global); if (!proto) return false; + } else { + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; } + RootedObject globalLexical(cx, &global->lexicalScope()); RootedFunction fun(cx, NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED_LAMBDA, globalLexical, @@ -2114,7 +2139,8 @@ js::NewFunctionWithProto(ExclusiveContext* cx, Native native, unsigned nargs, JSFunction::Flags flags, HandleObject enclosingDynamicScope, HandleAtom atom, HandleObject proto, gc::AllocKind allocKind /* = AllocKind::FUNCTION */, - NewObjectKind newKind /* = GenericObject */) + NewObjectKind newKind /* = GenericObject */, + NewFunctionProtoHandling protoHandling /* = NewFunctionClassProto */) { MOZ_ASSERT(allocKind == AllocKind::FUNCTION || allocKind == AllocKind::FUNCTION_EXTENDED); MOZ_ASSERT_IF(native, !enclosingDynamicScope); @@ -2126,8 +2152,14 @@ js::NewFunctionWithProto(ExclusiveContext* cx, Native native, // isSingleton implies isInterpreted. if (native && !IsAsmJSModuleNative(native)) newKind = SingletonObject; - funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto, allocKind, - newKind); + + if (protoHandling == NewFunctionClassProto) { + funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto, allocKind, + newKind); + } else { + funobj = NewObjectWithGivenTaggedProto(cx, &JSFunction::class_, AsTaggedProto(proto), + allocKind, newKind); + } if (!funobj) return nullptr; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index cc79e4e9c9..e844ddcff0 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -651,15 +651,23 @@ NewScriptedFunction(ExclusiveContext* cx, unsigned nargs, JSFunction::Flags flag NewObjectKind newKind = GenericObject, HandleObject enclosingDynamicScope = nullptr); -// If proto is nullptr, Function.prototype is used instead. If +// By default, if proto is nullptr, Function.prototype is used instead.i +// If protoHandling is NewFunctionExactProto, and proto is nullptr, the created +// function will use nullptr as its [[Prototype]] instead. If // enclosingDynamicScope is null, the function will have a null environment() // (yes, null, not the global). In all cases, the global will be used as the // parent. + +enum NewFunctionProtoHandling { + NewFunctionClassProto, + NewFunctionGivenProto +}; extern JSFunction* NewFunctionWithProto(ExclusiveContext* cx, JSNative native, unsigned nargs, JSFunction::Flags flags, HandleObject enclosingDynamicScope, HandleAtom atom, HandleObject proto, gc::AllocKind allocKind = gc::AllocKind::FUNCTION, - NewObjectKind newKind = GenericObject); + NewObjectKind newKind = GenericObject, + NewFunctionProtoHandling protoHandling = NewFunctionClassProto); extern JSAtom* IdToFunctionName(JSContext* cx, HandleId id); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index f41f9a65d9..c6db20ee15 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3662,16 +3662,16 @@ struct IsAboutToBeFinalizedFunctor { } }; +/* static */ bool +UniqueIdGCPolicy::needsSweep(Cell** cell, uint64_t*) +{ + return DispatchTraceKindTyped(IsAboutToBeFinalizedFunctor(), (*cell)->getTraceKind(), cell); +} + void JS::Zone::sweepUniqueIds(js::FreeOp* fop) { - for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront()) { - if (DispatchTraceKindTyped(IsAboutToBeFinalizedFunctor(), e.front().key()->getTraceKind(), - &e.front().mutableKey())) - { - e.removeFront(); - } - } + uniqueIds_.sweep(); } /* diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 817a3fb8d5..7d5b11054d 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -10,7 +10,6 @@ #include "jsmath.h" -#include "mozilla/Constants.h" #include "mozilla/FloatingPoint.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/MemoryReporting.h" diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 76c486d6f8..2250af8237 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -9,32 +9,9 @@ #include "mozilla/MemoryReporting.h" -#include "NamespaceImports.h" +#include -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif -#ifndef M_E -# define M_E 2.7182818284590452354 -#endif -#ifndef M_LOG2E -# define M_LOG2E 1.4426950408889634074 -#endif -#ifndef M_LOG10E -# define M_LOG10E 0.43429448190325182765 -#endif -#ifndef M_LN2 -# define M_LN2 0.69314718055994530942 -#endif -#ifndef M_LN10 -# define M_LN10 2.30258509299404568402 -#endif -#ifndef M_SQRT2 -# define M_SQRT2 1.41421356237309504880 -#endif -#ifndef M_SQRT1_2 -# define M_SQRT1_2 0.70710678118654752440 -#endif +#include "NamespaceImports.h" namespace js { diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 1b90adb581..68c70a028a 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -488,7 +488,11 @@ Number(JSContext* cx, unsigned argc, Value* vp) if (!isConstructing) return true; - JSObject* obj = NumberObject::create(cx, args.rval().toNumber()); + RootedObject newTarget(cx, &args.newTarget().toObject()); + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + JSObject* obj = NumberObject::create(cx, args.rval().toNumber(), proto); if (!obj) return false; args.rval().setObject(*obj); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 2523b4454f..b513015064 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -689,9 +689,9 @@ NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto return fill(entry, clasp, proto.raw(), kind, obj); } -static bool -NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle proto, - NewObjectKind newKind, const Class* clasp) +bool +js::NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle proto, + NewObjectKind newKind, const Class* clasp) { return cxArg->isJSContext() && proto.isObject() && @@ -883,11 +883,9 @@ js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj) JSObject* js::CreateThis(JSContext* cx, const Class* newclasp, HandleObject callee) { - RootedValue protov(cx); - if (!GetProperty(cx, callee, callee, cx->names().prototype, &protov)) + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, callee, &proto)) return nullptr; - - RootedObject proto(cx, protov.isObjectOrNull() ? protov.toObjectOrNull() : nullptr); gc::AllocKind kind = NewObjectGCKind(newclasp); return NewObjectWithClassProto(cx, newclasp, proto, kind); } @@ -990,16 +988,35 @@ js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObj return res; } +bool +js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, MutableHandleObject proto) +{ + RootedValue protov(cx); + if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) + return false; + proto.set(protov.isObject() ? &protov.toObject() : nullptr); + return true; +} + +bool +js::GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, MutableHandleObject proto) +{ + RootedObject newTarget(cx); + if (args.isConstructing()) + newTarget = &args.newTarget().toObject(); + else + newTarget = &args.callee(); + return GetPrototypeFromConstructor(cx, newTarget, proto); +} + JSObject* js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTarget, NewObjectKind newKind) { - RootedValue protov(cx); - if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) - return nullptr; RootedObject proto(cx); - if (protov.isObject()) - proto = &protov.toObject(); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return nullptr; + JSObject* obj = CreateThisForFunctionWithProto(cx, callee, newTarget, proto, newKind); if (obj && newKind == SingletonObject) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 705efbc4bb..0ea0ec5fff 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -693,18 +693,8 @@ namespace js { /*** Standard internal methods ******************************************************************** * - * The functions below are the fundamental operations on objects. - * - * ES6 specifies 14 internal methods that define how objects behave. The spec - * is actually quite good on this topic, though you may have to read it a few - * times. See ES6 draft rev 29 (6 Dec 2014) 6.1.7.2 and 6.1.7.3. - * - * When 'obj' is an ordinary object, these functions have boring standard - * behavior as specified by ES6 draft rev 29 section 9.1; see the section about - * internal methods in vm/NativeObject.h. - * - * Proxies override the behavior of internal methods. So when 'obj' is a proxy, - * any one of the functions below could do just about anything. See js/Proxy.h. + * The functions below are the fundamental operations on objects. See the + * comment about "Standard internal methods" in jsapi.h. */ /* @@ -752,8 +742,7 @@ extern bool PreventExtensions(JSContext* cx, HandleObject obj); /* - * ES6 [[GetOwnPropertyDescriptor]]. Get a description of one of obj's own - * properties. + * ES6 [[GetOwnProperty]]. Get a description of one of obj's own properties. * * If no such property exists on obj, return true with desc.object() set to * null. @@ -805,8 +794,8 @@ DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValu unsigned attrs = JSPROP_ENUMERATE); /* - * ES6 [[HasProperty]]. Set *foundp to true if `id in obj` (that is, if obj has - * an own or inherited property obj[id]), false otherwise. + * ES6 [[Has]]. Set *foundp to true if `id in obj` (that is, if obj has an own + * or inherited property obj[id]), false otherwise. */ inline bool HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); @@ -1100,6 +1089,17 @@ GetInitialHeap(NewObjectKind newKind, const Class* clasp) return gc::DefaultHeap; } +bool +NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle proto, + NewObjectKind newKind, const Class* clasp); + +// ES6 9.1.15 GetPrototypeFromConstructor. +extern bool +GetPrototypeFromConstructor(JSContext* cx, js::HandleObject newTarget, js::MutableHandleObject proto); + +extern bool +GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, js::MutableHandleObject proto); + // Specialized call for constructing |this| with a known function callee, // and a known prototype. extern JSObject* diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 9067a2bca0..0b352e6b3c 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -687,6 +687,24 @@ NewObjectWithClassProto(ExclusiveContext* cx, const Class* clasp, HandleObject p return NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind); } +template +inline T* +NewObjectWithClassProto(ExclusiveContext* cx, HandleObject proto, + NewObjectKind newKind = GenericObject) +{ + JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, newKind); + return obj ? &obj->as() : nullptr; +} + +template +inline T* +NewObjectWithClassProto(ExclusiveContext* cx, HandleObject proto, gc::AllocKind allocKind, + NewObjectKind newKind = GenericObject) +{ + JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, allocKind, newKind); + return obj ? &obj->as() : nullptr; +} + /* * Create a native instance of the given class with parent and proto set * according to the context's active global. diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 005bc13e5b..9761e2f4a8 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -130,7 +130,7 @@ js::StackUses(JSScript* script, jsbytecode* pc) return 2 + GET_ARGC(pc) + 1; default: /* stack: fun, this, [argc arguments] */ - MOZ_ASSERT(op == JSOP_CALL || op == JSOP_EVAL || + MOZ_ASSERT(op == JSOP_CALL || op == JSOP_EVAL || op == JSOP_CALLITER || op == JSOP_STRICTEVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY); return 2 + GET_ARGC(pc); } @@ -1167,6 +1167,7 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc) case JSOP_NEWTARGET: return write("new.target"); case JSOP_CALL: + case JSOP_CALLITER: case JSOP_FUNCALL: return decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 2)) && write("(...)"); diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 996f2629ad..cd80a06f5d 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -76,7 +76,7 @@ enum { JOF_CHECKSLOPPY = 1 << 19, /* Op can only be generated in sloppy mode */ JOF_CHECKSTRICT = 1 << 20, /* Op can only be generated in strict mode */ JOF_INVOKE = 1 << 21, /* JSOP_CALL, JSOP_FUNCALL, JSOP_FUNAPPLY, - JSOP_NEW, JSOP_EVAL */ + JSOP_NEW, JSOP_EVAL, JSOP_CALLITER */ /* 1 << 22 is unused */ /* 1 << 23 is unused */ /* 1 << 24 is unused */ diff --git a/js/src/jsprf.cpp b/js/src/jsprf.cpp index b148196709..ed8c35edc3 100644 --- a/js/src/jsprf.cpp +++ b/js/src/jsprf.cpp @@ -1010,10 +1010,8 @@ JS_PUBLIC_API(uint32_t) JS_vsnprintf(char* out, uint32_t outlen, const char* fmt, va_list ap) { SprintfState ss; - uint32_t n; - MOZ_ASSERT(int32_t(outlen) > 0); - if (int32_t(outlen) <= 0) + if (outlen == 0) return 0; ss.stuff = LimitStuff; @@ -1022,12 +1020,19 @@ JS_vsnprintf(char* out, uint32_t outlen, const char* fmt, va_list ap) ss.maxlen = outlen; (void) dosprintf(&ss, fmt, ap); - /* If we added chars, and we didn't append a null, do it now. */ - if (ss.cur != ss.base && ss.cur[-1] != '\0') - ss.cur[-1] = '\0'; + uint32_t charsWritten = ss.cur - ss.base; + MOZ_ASSERT(charsWritten > 0); - n = ss.cur - ss.base; - return n ? n - 1 : n; + // If we didn't append a null then we must have hit the buffer limit. Write + // a null terminator now and return a value indicating that we failed. + if (ss.cur[-1] != '\0') { + ss.cur[-1] = '\0'; + return outlen; + } + + // Success: return the number of character written excluding the null + // terminator. + return charsWritten - 1; } JS_PUBLIC_API(char*) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 08d5c2e2ea..d81d7cf6ca 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -625,6 +625,8 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript HasLazyScript, HasNonSyntacticScope, HasInnerFunctions, + NeedsHomeObject, + IsDerivedClassConstructor, }; uint32_t length, lineno, column, nslots; @@ -764,6 +766,10 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript scriptBits |= (1 << HasNonSyntacticScope); if (script->hasInnerFunctions()) scriptBits |= (1 << HasInnerFunctions); + if (script->needsHomeObject()) + scriptBits |= (1 << NeedsHomeObject); + if (script->isDerivedClassConstructor()) + scriptBits |= (1 << IsDerivedClassConstructor); } if (!xdr->codeUint32(&prologueLength)) @@ -904,6 +910,10 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript script->hasNonSyntacticScope_ = true; if (scriptBits & (1 << HasInnerFunctions)) script->hasInnerFunctions_ = true; + if (scriptBits & (1 << NeedsHomeObject)) + script->needsHomeObject_ = true; + if (scriptBits & (1 << IsDerivedClassConstructor)) + script->isDerivedClassConstructor_ = true; if (scriptBits & (1 << IsLegacyGenerator)) { MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator))); @@ -2847,9 +2857,9 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t ncon } if (script->bindings.count() != 0) { - // Make sure bindings are sufficiently aligned. - cursor = reinterpret_cast - (JS_ROUNDUP(reinterpret_cast(cursor), JS_ALIGNMENT_OF(Binding))); + // Make sure bindings are sufficiently aligned. + cursor = reinterpret_cast + (JS_ROUNDUP(reinterpret_cast(cursor), JS_ALIGNMENT_OF(Binding))); } cursor = script->bindings.switchToScriptStorage(reinterpret_cast(cursor)); @@ -3427,7 +3437,7 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri /* Script data */ size_t size = src->dataSize(); - uint8_t* data = AllocScriptData(cx->zone(), size); + ScopedJSFreePtr data(AllocScriptData(cx->zone(), size)); if (size && !data) { ReportOutOfMemory(cx); return false; @@ -3526,9 +3536,9 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri dst->bindings = bindings; /* This assignment must occur before all the Rebase calls. */ - dst->data = data; + dst->data = data.forget(); dst->dataSize_ = size; - memcpy(data, src->data, size); + memcpy(dst->data, src->data, size); /* Script filenames, bytecodes and atoms are runtime-wide. */ dst->setCode(src->code()); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index d2e154b917..68819ba4f0 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -9,6 +9,7 @@ #ifndef jsscript_h #define jsscript_h +#include "mozilla/Atomics.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" #include "mozilla/UniquePtr.h" @@ -2380,7 +2381,7 @@ struct SharedScriptData { uint32_t length; uint32_t natoms; - bool marked; + mozilla::Atomic marked; jsbytecode data[1]; static SharedScriptData* new_(ExclusiveContext* cx, uint32_t codeLength, diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index ff68dcdcf4..716931d75d 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3481,7 +3481,7 @@ LambdaIsGetElem(JSContext* cx, JSObject& lambda, MutableHandleNativeObject pobj) return true; RootedFunction fun(cx, &lambda.as()); - if (!fun->isInterpreted()) + if (!fun->isInterpreted() || fun->isClassConstructor()) return true; JSScript* script = fun->getOrCreateScript(cx); @@ -4079,7 +4079,12 @@ js::StringConstructor(JSContext* cx, unsigned argc, Value* vp) } if (args.isConstructing()) { - StringObject* strobj = StringObject::create(cx, str); + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + StringObject* strobj = StringObject::create(cx, str, proto); if (!strobj) return false; args.rval().setObject(*strobj); diff --git a/js/src/make-source-package.sh b/js/src/make-source-package.sh index 084ec16fe5..3c9251a48d 100755 --- a/js/src/make-source-package.sh +++ b/js/src/make-source-package.sh @@ -1,10 +1,31 @@ -#!/bin/sh +#!/bin/bash # Find out ASAP if some command breaks here, because we're copying a lot of # files we don't actually maintain ourselves, and requirements could easily be # broken. set -e +: ${MAKE:=make} +: ${MKDIR:=mkdir} +: ${TAR:=tar} +: ${SRCDIR:=$(cd $(dirname $0); pwd 2>/dev/null)} +: ${MOZJS_NAME:=mozjs} +: ${DIST:=/tmp/mozjs-src-pkg} + +if [[ -f "$SRCDIR/../../config/milestone.txt" ]]; then + MILESTONE="$(tail -1 $SRCDIR/../../config/milestone.txt)" + IFS=. read -a VERSION < <(echo "$MILESTONE") + MOZJS_MAJOR_VERSION=${MOZJS_MAJOR_VERSION:-${VERSION[0]}} + MOZJS_MINOR_VERSION=${MOZJS_MINOR_VERSION:-${VERSION[1]}} + MOZJS_PATCH_VERSION=${MOZJS_PATCH_VERSION:-${VERSION[2]}} +fi + +cmd=${1:-build} +pkg="${MOZJS_NAME}-${MOZJS_MAJOR_VERSION}.${MOZJS_MINOR_VERSION}.${MOZJS_PATCH_VERSION:-${MOZJS_ALPHA:-0}}.tar.bz2" +pkgpath=${pkg%.tar*} +tgtpath=${DIST}/${pkgpath} +taropts="-jcf" + # need these environment vars: echo "Environment:" echo " MAKE = $MAKE" @@ -12,16 +33,14 @@ echo " MKDIR = $MKDIR" echo " TAR = $TAR" echo " DIST = $DIST" echo " SRCDIR = $SRCDIR" +echo " MOZJS_NAME = $MOZJS_NAME" echo " MOZJS_MAJOR_VERSION = $MOZJS_MAJOR_VERSION" echo " MOZJS_MINOR_VERSION = $MOZJS_MINOR_VERSION" echo " MOZJS_PATCH_VERSION = $MOZJS_PATCH_VERSION" echo " MOZJS_ALPHA = $MOZJS_ALPHA" +echo "" -cmd=${1:-build} -pkg="mozjs-${MOZJS_MAJOR_VERSION}.${MOZJS_MINOR_VERSION}.${MOZJS_PATCH_VERSION:-${MOZJS_ALPHA:-0}}.tar.bz2" -pkgpath=${pkg%.tar*} -tgtpath=${DIST}/${pkgpath} -taropts="-jcf" +TOPSRCDIR=${SRCDIR}/../.. case $cmd in "clean") @@ -29,11 +48,14 @@ case $cmd in rm -rf ${pkg} ${tgtpath} ;; "build") - # Ensure that the configure script is newer than the configure.in script. - if [ ${SRCDIR}/configure.in -nt ${SRCDIR}/configure ]; then - echo "error: js/src/configure is out of date. Please regenerate before packaging." >&2 - exit 1 - fi + echo -n "Press enter to build $pkg> " + read + + # Ensure that the configure script is newer than the configure.in script. + if [ ${SRCDIR}/configure.in -nt ${SRCDIR}/configure ]; then + echo "error: js/src/configure is out of date. Please regenerate before packaging." >&2 + exit 1 + fi echo "Packaging source tarball ${pkg}..." if [ -d ${tgtpath} ]; then @@ -43,58 +65,79 @@ case $cmd in # copy the embedded icu ${MKDIR} -p ${tgtpath}/intl - cp -t ${tgtpath}/intl -dRp ${SRCDIR}/../../intl/icu + cp -pPR ${TOPSRCDIR}/intl/icu ${tgtpath}/intl # copy main moz.build and Makefile.in - cp -t ${tgtpath} -dRp ${SRCDIR}/../../Makefile.in ${SRCDIR}/../../moz.build + cp -pPR ${TOPSRCDIR}/Makefile.in ${TOPSRCDIR}/moz.build ${tgtpath} - # copy a nspr file used by the build system - ${MKDIR} -p ${tgtpath}/nsprpub/config - cp -t ${tgtpath}/nsprpub/config -dRp \ - ${SRCDIR}/../../nsprpub/config/make-system-wrappers.pl + # copy nspr. + cp -pPR ${SRCDIR}/../../nsprpub ${tgtpath} # copy build and config directory. - cp -t ${tgtpath} -dRp ${SRCDIR}/../../build ${SRCDIR}/../../config + cp -pPR ${TOPSRCDIR}/build ${TOPSRCDIR}/config ${tgtpath} # put in js itself - cp -t ${tgtpath} -dRp ${SRCDIR}/../../mfbt - cp -t ${tgtpath}/js -dRp ${SRCDIR}/../public + cp -pPR ${TOPSRCDIR}/mfbt ${tgtpath} + cp -pPR ${SRCDIR}/../public ${tgtpath}/js find ${SRCDIR} -mindepth 1 -maxdepth 1 -not -path ${DIST} -a -not -name ${pkg} \ - -exec cp -t ${tgtpath}/js/src -dRp {} + + -exec cp -pPR {} ${tgtpath}/js/src \; # distclean if necessary if [ -e ${tgtpath}/js/src/Makefile ]; then ${MAKE} -C ${tgtpath}/js/src distclean fi - cp -t ${tgtpath} -dRp \ - ${SRCDIR}/../../python + cp -pPR \ + ${TOPSRCDIR}/python \ + ${tgtpath} ${MKDIR} -p ${tgtpath}/dom/bindings - cp -t ${tgtpath}/dom/bindings -dRp \ - ${SRCDIR}/../../dom/bindings/mozwebidlcodegen + cp -pPR \ + ${TOPSRCDIR}/dom/bindings/mozwebidlcodegen \ + ${tgtpath}/dom/bindings ${MKDIR} -p ${tgtpath}/media/webrtc/trunk/tools - cp -t ${tgtpath}/media/webrtc/trunk/tools -dRp \ - ${SRCDIR}/../../media/webrtc/trunk/tools/gyp + cp -pPR \ + ${TOPSRCDIR}/media/webrtc/trunk/tools/gyp \ + ${tgtpath}/media/webrtc/trunk/tools ${MKDIR} -p ${tgtpath}/testing - cp -t ${tgtpath}/testing -dRp \ - ${SRCDIR}/../../testing/mozbase - ${MKDIR} -p ${tgtpath}/modules/zlib - cp -t ${tgtpath}/modules/zlib -dRp \ - ${SRCDIR}/../../modules/zlib/src + cp -pPR \ + ${TOPSRCDIR}/testing/mozbase \ + ${tgtpath}/testing + ${MKDIR} -p ${tgtpath}/modules + cp -pPR \ + ${TOPSRCDIR}/modules/zlib/src \ + ${tgtpath}/modules ${MKDIR} -p ${tgtpath}/layout/tools/reftest - cp -t ${tgtpath}/layout/tools/reftest -dRp \ - ${SRCDIR}/../../layout/tools/reftest/reftest + cp -pPR \ + ${TOPSRCDIR}/layout/tools/reftest/reftest \ + ${tgtpath}/layout/tools/reftest ${MKDIR} -p ${tgtpath}/toolkit/mozapps/installer - cp -t ${tgtpath}/toolkit/mozapps/installer -dRp \ - ${SRCDIR}/../../toolkit/mozapps/installer/package-name.mk \ - ${SRCDIR}/../../toolkit/mozapps/installer/upload-files.mk \ + cp -pPR \ + ${TOPSRCDIR}/toolkit/mozapps/installer/package-name.mk \ + ${TOPSRCDIR}/toolkit/mozapps/installer/upload-files.mk \ + ${tgtpath}/toolkit/mozapps/installer + ${MKDIR} -p ${tgtpath}/mozglue + cp -pPR \ + ${TOPSRCDIR}/mozglue/build \ + ${TOPSRCDIR}/mozglue/crt \ + ${TOPSRCDIR}/mozglue/misc \ + ${TOPSRCDIR}/mozglue/moz.build \ + ${tgtpath}/mozglue + ${MKDIR} -p ${tgtpath}/memory + cp -pPR \ + ${TOPSRCDIR}/memory/moz.build \ + ${TOPSRCDIR}/memory/build \ + ${TOPSRCDIR}/memory/fallible \ + ${TOPSRCDIR}/memory/jemalloc \ + ${TOPSRCDIR}/memory/mozalloc \ + ${TOPSRCDIR}/memory/mozjemalloc \ + ${tgtpath}/memory # remove *.pyc and *.pyo files if any find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" |xargs rm -f # copy or create INSTALL - if [ -e {DIST}/INSTALL ]; then - cp -t ${tgtpath} ${DIST}/INSTALL + if [ -e ${DIST}/INSTALL ]; then + cp ${DIST}/INSTALL ${tgtpath} else cat <${tgtpath}/INSTALL Full build documentation for SpiderMonkey is hosted on MDN: @@ -106,14 +149,16 @@ be run over the binaries before deploying them. Building with default options may be performed as follows: cd js/src - ./configure - make + mkdir obj + cd obj + ../configure + make # or mozmake on Windows INSTALL_EOF fi # copy or create README if [ -e ${DIST}/README ]; then - cp -t ${tgtpath} ${DIST}/README + cp ${DIST}/README ${tgtpath} else cat <${tgtpath}/README This directory contains SpiderMonkey ${MOZJS_MAJOR_VERSION}. @@ -136,14 +181,14 @@ README_EOF # copy patches dir, if it currently exists in DIST if [ -d ${DIST}/patches ]; then - cp -t ${tgtpath} -dRp ${DIST}/patches + cp -pPR ${DIST}/patches ${tgtpath} elif [ -d ${SRCDIR}/../../patches ]; then - cp -t ${tgtpath} -dRp ${SRCDIR}/../../patches + cp -pPR ${TOPSRCDIR}/patches ${tgtpath} fi # Roll the tarball ${TAR} $taropts ${DIST}/../${pkg} -C ${DIST} ${pkgpath} - echo "done." + echo "Wrote $(cd ${DIST}/..; echo $PWD)/${pkg}" ;; *) echo "Unrecognized command: $cmd" diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index d6443a1628..20a37ff88d 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -6227,29 +6227,29 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op) .setUnboxedArrays(enableUnboxedArrays); if (op.getBoolOption("no-unboxed-objects")) - jit::js_JitOptions.disableUnboxedObjects = true; + jit::JitOptions.disableUnboxedObjects = true; if (const char* str = op.getStringOption("ion-scalar-replacement")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableScalarReplacement = false; + jit::JitOptions.disableScalarReplacement = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableScalarReplacement = true; + jit::JitOptions.disableScalarReplacement = true; else return OptionFailure("ion-scalar-replacement", str); } if (const char* str = op.getStringOption("ion-shared-stubs")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableSharedStubs = false; + jit::JitOptions.disableSharedStubs = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableSharedStubs = true; + jit::JitOptions.disableSharedStubs = true; else return OptionFailure("ion-shared-stubs", str); } if (const char* str = op.getStringOption("ion-gvn")) { if (strcmp(str, "off") == 0) { - jit::js_JitOptions.disableGvn = true; + jit::JitOptions.disableGvn = true; } else if (strcmp(str, "on") != 0 && strcmp(str, "optimistic") != 0 && strcmp(str, "pessimistic") != 0) @@ -6262,128 +6262,128 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op) if (const char* str = op.getStringOption("ion-licm")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableLicm = false; + jit::JitOptions.disableLicm = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableLicm = true; + jit::JitOptions.disableLicm = true; else return OptionFailure("ion-licm", str); } if (const char* str = op.getStringOption("ion-edgecase-analysis")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableEdgeCaseAnalysis = false; + jit::JitOptions.disableEdgeCaseAnalysis = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableEdgeCaseAnalysis = true; + jit::JitOptions.disableEdgeCaseAnalysis = true; else return OptionFailure("ion-edgecase-analysis", str); } if (const char* str = op.getStringOption("ion-pgo")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disablePgo = false; + jit::JitOptions.disablePgo = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disablePgo = true; + jit::JitOptions.disablePgo = true; else return OptionFailure("ion-pgo", str); } if (const char* str = op.getStringOption("ion-range-analysis")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableRangeAnalysis = false; + jit::JitOptions.disableRangeAnalysis = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableRangeAnalysis = true; + jit::JitOptions.disableRangeAnalysis = true; else return OptionFailure("ion-range-analysis", str); } if (const char *str = op.getStringOption("ion-sincos")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableSincos = false; + jit::JitOptions.disableSincos = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableSincos = true; + jit::JitOptions.disableSincos = true; else return OptionFailure("ion-sincos", str); } if (const char* str = op.getStringOption("ion-sink")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableSink = false; + jit::JitOptions.disableSink = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableSink = true; + jit::JitOptions.disableSink = true; else return OptionFailure("ion-sink", str); } if (const char* str = op.getStringOption("ion-loop-unrolling")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableLoopUnrolling = false; + jit::JitOptions.disableLoopUnrolling = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableLoopUnrolling = true; + jit::JitOptions.disableLoopUnrolling = true; else return OptionFailure("ion-loop-unrolling", str); } if (const char* str = op.getStringOption("ion-instruction-reordering")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableInstructionReordering = false; + jit::JitOptions.disableInstructionReordering = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableInstructionReordering = true; + jit::JitOptions.disableInstructionReordering = true; else return OptionFailure("ion-instruction-reordering", str); } if (op.getBoolOption("ion-check-range-analysis")) - jit::js_JitOptions.checkRangeAnalysis = true; + jit::JitOptions.checkRangeAnalysis = true; if (op.getBoolOption("ion-extra-checks")) - jit::js_JitOptions.runExtraChecks = true; + jit::JitOptions.runExtraChecks = true; if (const char* str = op.getStringOption("ion-inlining")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.disableInlining = false; + jit::JitOptions.disableInlining = false; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.disableInlining = true; + jit::JitOptions.disableInlining = true; else return OptionFailure("ion-inlining", str); } if (const char* str = op.getStringOption("ion-osr")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.osr = true; + jit::JitOptions.osr = true; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.osr = false; + jit::JitOptions.osr = false; else return OptionFailure("ion-osr", str); } if (const char* str = op.getStringOption("ion-limit-script-size")) { if (strcmp(str, "on") == 0) - jit::js_JitOptions.limitScriptSize = true; + jit::JitOptions.limitScriptSize = true; else if (strcmp(str, "off") == 0) - jit::js_JitOptions.limitScriptSize = false; + jit::JitOptions.limitScriptSize = false; else return OptionFailure("ion-limit-script-size", str); } int32_t warmUpThreshold = op.getIntOption("ion-warmup-threshold"); if (warmUpThreshold >= 0) - jit::js_JitOptions.setCompilerWarmUpThreshold(warmUpThreshold); + jit::JitOptions.setCompilerWarmUpThreshold(warmUpThreshold); warmUpThreshold = op.getIntOption("baseline-warmup-threshold"); if (warmUpThreshold >= 0) - jit::js_JitOptions.baselineWarmUpThreshold = warmUpThreshold; + jit::JitOptions.baselineWarmUpThreshold = warmUpThreshold; if (op.getBoolOption("baseline-eager")) - jit::js_JitOptions.baselineWarmUpThreshold = 0; + jit::JitOptions.baselineWarmUpThreshold = 0; if (const char* str = op.getStringOption("ion-regalloc")) { - jit::js_JitOptions.forcedRegisterAllocator = jit::LookupRegisterAllocator(str); - if (!jit::js_JitOptions.forcedRegisterAllocator.isSome()) + jit::JitOptions.forcedRegisterAllocator = jit::LookupRegisterAllocator(str); + if (!jit::JitOptions.forcedRegisterAllocator.isSome()) return OptionFailure("ion-regalloc", str); } if (op.getBoolOption("ion-eager")) - jit::js_JitOptions.setEagerCompilation(); + jit::JitOptions.setEagerCompilation(); offthreadCompilation = true; if (const char* str = op.getStringOption("ion-offthread-compile")) { diff --git a/js/src/tests/ecma_6/Class/boundFunctionSubclassing.js b/js/src/tests/ecma_6/Class/boundFunctionSubclassing.js new file mode 100644 index 0000000000..fa5544f8d3 --- /dev/null +++ b/js/src/tests/ecma_6/Class/boundFunctionSubclassing.js @@ -0,0 +1,24 @@ +var test = ` + +class func extends Function { } +let inst = new func("x", "return this.bar + x"); + +// First, ensure that we get sane prototype chains for the bound instance +let bound = inst.bind({bar: 3}, 4); +assertEq(bound instanceof func, true); +assertEq(bound(), 7); + +// Check the corner case for Function.prototype.bind where the function has +// a null [[Prototype]] +Object.setPrototypeOf(inst, null); +bound = Function.prototype.bind.call(inst, {bar:1}, 3); +assertEq(Object.getPrototypeOf(bound), null); +assertEq(bound(), 4); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/bytecodePatternMatching.js b/js/src/tests/ecma_6/Class/bytecodePatternMatching.js new file mode 100644 index 0000000000..0c16069911 --- /dev/null +++ b/js/src/tests/ecma_6/Class/bytecodePatternMatching.js @@ -0,0 +1,34 @@ +// Constructors can't be called so we can't pattern match +// them in replace and sort. +var test = ` +function a() { + var b = {a: "A"}; + + class X { + constructor(a) { + return b[a] + } + }; + + assertThrowsInstanceOf(() => "a".replace(/a/, X), TypeError); +} + +function b() { + class X { + constructor(x, y) { + return x - y; + } + } + + assertThrowsInstanceOf(() => [1, 2, 3].sort(X), TypeError); +} + +a(); +b(); +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "OK"); diff --git a/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js b/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js new file mode 100644 index 0000000000..4715dc412d --- /dev/null +++ b/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js @@ -0,0 +1,112 @@ +var test = ` + +function testBuiltinInstanceIsInstanceOf(instance, builtin, class_) { + assertEq(instance instanceof class_, true); + assertEq(instance instanceof builtin, true); + + if (builtin === Array) + assertEq(Array.isArray(instance), true); +} + +function testBuiltinInstance(builtin, ...args) { + class sub extends builtin { + constructor(...args) { + super(...args); + this.called = true; + } + } + + let instance = new sub(...args); + assertEq(instance.called, true); + testBuiltinInstanceIsInstanceOf(instance, builtin, sub); +} + +function testBuiltinMultipleSubclasses(builtin, ...args) { + function f(obj, prop) { + assertEq(obj.prop, prop); + } + + class sub1 extends builtin { }; + class sub2 extends builtin { }; + + const prop1 = "A"; + const prop2 = "B"; + + sub1.prototype.prop = prop1; + sub2.prototype.prop = prop2; + + let instance1 = new sub1(...args); + let instance2 = new sub2(...args); + + // Also make sure we get the properties we want with a default constructor + testBuiltinInstanceIsInstanceOf(instance1, builtin, sub1); + + for (let i = 0; i < 10; i++) { + f(instance1, prop1); + f(instance2, prop2); + } +} + +function testBuiltin(builtin, ...args) { + testBuiltinInstance(builtin, ...args); + testBuiltinMultipleSubclasses(builtin, ...args); +} + +function testBuiltinTypedArrays() { + let typedArrays = [Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array]; + + for (let array of typedArrays) { + testBuiltin(array); + testBuiltin(array, 5); + testBuiltin(array, new array()); + testBuiltin(array, new ArrayBuffer()); + } +} + +testBuiltin(Function); +testBuiltin(Object); +testBuiltin(Boolean); +testBuiltin(Error); +testBuiltin(EvalError); +testBuiltin(RangeError); +testBuiltin(ReferenceError); +testBuiltin(SyntaxError); +testBuiltin(TypeError); +testBuiltin(URIError); +testBuiltin(Number); +testBuiltin(Date); +testBuiltin(Date, 5); +testBuiltin(Date, 5, 10); +testBuiltin(RegExp); +testBuiltin(RegExp, /Regexp Argument/); +testBuiltin(RegExp, "String Argument"); +testBuiltin(Map); +testBuiltin(Set); +testBuiltin(WeakMap); +testBuiltin(WeakSet); +testBuiltin(ArrayBuffer); +testBuiltinTypedArrays(); +testBuiltin(DataView, new ArrayBuffer()); +testBuiltin(DataView, new (newGlobal().ArrayBuffer)()); +testBuiltin(String); +testBuiltin(Array); +testBuiltin(Array, 15); +testBuiltin(Array, 3.0); +testBuiltin(Array, "non-length one-arg"); +testBuiltin(Array, 5, 10, 15, "these are elements"); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js b/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js new file mode 100644 index 0000000000..00e812e945 --- /dev/null +++ b/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js @@ -0,0 +1,29 @@ +var test = ` + +class foo extends Array { } + +function testArrs(arrs) { + for (let arr of arrs) { + assertEq(Object.getPrototypeOf(arr), foo.prototype); + } +} + +var arrs = []; +for (var i = 0; i < 25; i++) + arrs.push(new foo(1)); + +testArrs(arrs); + +arrs[0].nonIndexedProp = "uhoh"; + +arrs.push(new foo(1)); + +testArrs(arrs); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/superCallBaseInvoked.js b/js/src/tests/ecma_6/Class/superCallBaseInvoked.js index 807b132000..59ef23650d 100644 --- a/js/src/tests/ecma_6/Class/superCallBaseInvoked.js +++ b/js/src/tests/ecma_6/Class/superCallBaseInvoked.js @@ -53,8 +53,6 @@ testBase(p); handler.construct = (target, args, nt) => Reflect.construct(target, args, nt); testBase(p); -// Object will have to wait for fixed builtins. - `; if (classesEnabled()) diff --git a/js/src/tests/ecma_6/DataView/detach-after-construction.js b/js/src/tests/ecma_6/DataView/detach-after-construction.js new file mode 100644 index 0000000000..6185b2a1d0 --- /dev/null +++ b/js/src/tests/ecma_6/DataView/detach-after-construction.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!xulRuntime.shell) + +for (var neuterArg of ['change-data', 'same-data']) { + var buf = new ArrayBuffer([1,2]); + var bufView = new DataView(buf); + + neuter(buf, neuterArg); + + assertThrowsInstanceOf(()=>bufView.getInt8(0), TypeError); +} + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Map/iterable.js b/js/src/tests/ecma_6/Map/iterable.js new file mode 100644 index 0000000000..cf6b7228ab --- /dev/null +++ b/js/src/tests/ecma_6/Map/iterable.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +let length; +let iterable = { + [Symbol.iterator]() { return this; }, + next() { length = arguments.length; return {done: true}; } +}; + +new Map(iterable); +// ensure no arguments are passed to next() during construction (Bug 1197095) +assertEq(length, 0); + +let typeofThis; +Object.defineProperty(Number.prototype, Symbol.iterator, { + value() { + "use strict"; + typeofThis = typeof this; + return { next() { return {done: true}; } }; + } +}); + +new Map(0); +// ensure that iterable objects retain their type (Bug 1197094) +assertEq(typeofThis, "number"); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/construct.js b/js/src/tests/ecma_6/Reflect/construct.js index 870dcb3302..5a62bac432 100644 --- a/js/src/tests/ecma_6/Reflect/construct.js +++ b/js/src/tests/ecma_6/Reflect/construct.js @@ -111,9 +111,7 @@ for (var v of SOME_PRIMITIVE_VALUES.concat(nonConstructors)) { // creates a real array object. function someConstructor() {} var result = Reflect.construct(Array, [], someConstructor); -assertEq(Reflect.getPrototypeOf(result), - Array.prototype, // should be someConstructor.prototype, per ES6 22.1.1.1 Array() - "Congratulations on implementing Array subclassing! Fix this test for +1 karma point."); +assertEq(Reflect.getPrototypeOf(result), someConstructor.prototype); assertEq(result.length, 0); assertEq(Array.isArray(result), true); diff --git a/js/src/tests/ecma_6/RegExp/constructor-ordering-2.js b/js/src/tests/ecma_6/RegExp/constructor-ordering-2.js new file mode 100644 index 0000000000..21a6bbeca7 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/constructor-ordering-2.js @@ -0,0 +1,21 @@ +// Make sure that we don't ToString the second argument until /after/ doing +// the appropriate subclassing lookups + +var didLookup = false; + +var re = /a/; +var flags = { toString() { assertEq(didLookup, true); return "g"; } }; +var newRe = Reflect.construct(RegExp, [re, flags], + Object.defineProperty(function(){}.bind(null), "prototype", { + get() { + didLookup = true; + return RegExp.prototype; + } +})); + +assertEq(Object.getPrototypeOf(newRe), RegExp.prototype); +assertEq(didLookup, true); + + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/RegExp/constructor-ordering.js b/js/src/tests/ecma_6/RegExp/constructor-ordering.js new file mode 100644 index 0000000000..3e3a9b695b --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/constructor-ordering.js @@ -0,0 +1,16 @@ +// Make sure that we don't misorder subclassing accesses with respect to +// accessing regex arg internal slots +// +// Test credit André Bargull. + +var re = /a/; +var newRe = Reflect.construct(RegExp, [re], Object.defineProperty(function(){}.bind(null), "prototype", { + get() { + re.compile("b"); + return RegExp.prototype; + } +})); +assertEq(newRe.source, "a"); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/TypedArray/constructor-non-detached.js b/js/src/tests/ecma_6/TypedArray/constructor-non-detached.js new file mode 100644 index 0000000000..c3054256de --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/constructor-non-detached.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!xulRuntime.shell) + +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array +]; + +for (var constructor of constructors) { + for (var neuterType of ["change-data", "same-data"]) { + var buf = new constructor(); + neuter(buf.buffer, neuterType); + assertThrowsInstanceOf(()=> new constructor(buf), TypeError); + + var buffer = new ArrayBuffer(); + neuter(buffer, neuterType); + assertThrowsInstanceOf(()=> new constructor(buffer), TypeError); + } +} + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/js1_5/Error/constructor-ordering.js b/js/src/tests/js1_5/Error/constructor-ordering.js new file mode 100644 index 0000000000..dbcc2e6048 --- /dev/null +++ b/js/src/tests/js1_5/Error/constructor-ordering.js @@ -0,0 +1,17 @@ +var order = 0; +function assertOrdering(ordering) { + assertEq(order, ordering); + order++; +} + +// Spec mandates that the prototype is looked up /before/ we toString the +// argument. +var handler = { get() { assertOrdering(0); return Error.prototype } }; +var errorProxy = new Proxy(Error, handler); + +var toStringable = { toString() { assertOrdering(1); return "Argument"; } }; + +new errorProxy(toStringable); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/js1_8_5/extensions/decompile-for-of.js b/js/src/tests/js1_8_5/extensions/decompile-for-of.js index aab32da149..b99e20a40b 100644 --- a/js/src/tests/js1_8_5/extensions/decompile-for-of.js +++ b/js/src/tests/js1_8_5/extensions/decompile-for-of.js @@ -1,7 +1,7 @@ // The decompiler can handle the implicit call to @@iterator in a for-of loop. var x; -function check(code) { +function check(code, msg) { var s = "no exception thrown"; try { eval(code); @@ -9,19 +9,19 @@ function check(code) { s = exc.message; } - assertEq(s, `x[Symbol.iterator] is not a function`); + assertEq(s, msg); } x = {}; -check("for (var v of x) throw fit;"); -check("[...x]"); -check("Math.hypot(...x)"); +check("for (var v of x) throw fit;", "x is not iterable"); +check("[...x]", "x is not iterable"); +check("Math.hypot(...x)", "x is not iterable"); x[Symbol.iterator] = "potato"; -check("for (var v of x) throw fit;"); +check("for (var v of x) throw fit;", "x is not iterable"); x[Symbol.iterator] = {}; -check("for (var v of x) throw fit;"); +check("for (var v of x) throw fit;", "x[Symbol.iterator] is not a function"); if (typeof reportCompare === "function") reportCompare(0, 0, "ok"); diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index af2c018f7d..45b2c7cc2d 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -39,7 +39,7 @@ ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, ArgumentsObjec ArgumentsData* data) { JSScript* script = frame.script(); - if (frame.fun()->needsCallObject() && script->argsObjAliasesFormals()) { + if (frame.fun()->needsCallObject() && script->argumentsAliasesFormals()) { obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(frame.callObj())); for (AliasedFormalIter fi(script); fi; fi++) data->args[fi.frameIndex()] = MagicScopeSlotValue(fi.scopeSlot()); @@ -52,7 +52,7 @@ ArgumentsObject::MaybeForwardToCallObject(jit::JitFrameLayout* frame, HandleObje { JSFunction* callee = jit::CalleeTokenToFunction(frame->calleeToken()); JSScript* script = callee->nonLazyScript(); - if (callee->needsCallObject() && script->argsObjAliasesFormals()) { + if (callee->needsCallObject() && script->argumentsAliasesFormals()) { MOZ_ASSERT(callObj && callObj->is()); obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(*callObj.get())); for (AliasedFormalIter fi(script); fi; fi++) diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index d9f5c6118a..b68c73d011 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -476,7 +476,12 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) return false; } - JSObject* bufobj = create(cx, uint32_t(nbytes)); + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + JSObject* bufobj = create(cx, uint32_t(nbytes), proto); if (!bufobj) return false; args.rval().setObject(*bufobj); @@ -795,6 +800,7 @@ ArrayBufferObject::setFlags(uint32_t flags) ArrayBufferObject* ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents contents, OwnsState ownsState /* = OwnsData */, + HandleObject proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */) { MOZ_ASSERT_IF(contents.kind() == MAPPED, contents); @@ -835,7 +841,8 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content gc::AllocKind allocKind = GetGCObjectKind(nslots); AutoSetNewObjectMetadata metadata(cx); - Rooted obj(cx, NewBuiltinClassInstance(cx, allocKind, newKind)); + Rooted obj(cx, + NewObjectWithClassProto(cx, proto, allocKind, newKind)); if (!obj) { if (allocated) js_free(contents.data()); @@ -858,9 +865,11 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content ArrayBufferObject* ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, + HandleObject proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */) { - return create(cx, nbytes, BufferContents::createPlain(nullptr)); + return create(cx, nbytes, BufferContents::createPlain(nullptr), + OwnsState::OwnsData, proto); } JSObject* @@ -892,21 +901,25 @@ ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args /* * This method is only called for |DataView(alienBuf, ...)| which calls - * this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|, - * ergo there must be at least two arguments. + * this as |createDataViewForThis.call(alienBuf, byteOffset, byteLength, + * DataView.prototype)|, + * ergo there must be exactly 3 arguments. */ - MOZ_ASSERT(args.length() >= 2); + MOZ_ASSERT(args.length() == 3); - Rooted proto(cx, &args[args.length() - 1].toObject()); - - Rooted buffer(cx, &args.thisv().toObject()); + uint32_t byteOffset = args[0].toPrivateUint32(); + uint32_t byteLength = args[1].toPrivateUint32(); + Rooted buffer(cx, &args.thisv().toObject().as()); /* * Pop off the passed-along prototype and delegate to normal DataViewObject * construction. */ - CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base()); - return DataViewObject::construct(cx, buffer, frobbedArgs, proto); + JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, &args[2].toObject()); + if (!obj) + return false; + args.rval().setObject(*obj); + return true; } bool @@ -1125,7 +1138,7 @@ InnerViewTable::removeViews(ArrayBufferObject* buffer) map.remove(p); } -bool +/* static */ bool InnerViewTable::sweepEntry(JSObject** pkey, ViewVector& views) { if (IsAboutToBeFinalizedUnbarriered(pkey)) @@ -1143,21 +1156,14 @@ InnerViewTable::sweepEntry(JSObject** pkey, ViewVector& views) } void -InnerViewTable::sweep(JSRuntime* rt) +InnerViewTable::sweep() { MOZ_ASSERT(nurseryKeys.empty()); - - if (!map.initialized()) - return; - - for (Map::Enum e(map); !e.empty(); e.popFront()) { - if (sweepEntry(&e.front().mutableKey(), e.front().value())) - e.removeFront(); - } + map.sweep(); } void -InnerViewTable::sweepAfterMinorGC(JSRuntime* rt) +InnerViewTable::sweepAfterMinorGC() { MOZ_ASSERT(needsSweepAfterMinorGC()); @@ -1175,7 +1181,7 @@ InnerViewTable::sweepAfterMinorGC(JSRuntime* rt) } else { // Do the required sweeping by looking at every map entry. nurseryKeys.clear(); - sweep(rt); + sweep(); nurseryKeysValid = true; } @@ -1414,7 +1420,8 @@ JS_NewArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data) MOZ_ASSERT_IF(!data, nbytes == 0); ArrayBufferObject::BufferContents contents = ArrayBufferObject::BufferContents::create(data); - return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, TenuredObject); + return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, + /* proto = */ nullptr, TenuredObject); } JS_FRIEND_API(bool) @@ -1479,7 +1486,8 @@ JS_NewMappedArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data) MOZ_ASSERT(data); ArrayBufferObject::BufferContents contents = ArrayBufferObject::BufferContents::create(data); - return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, TenuredObject); + return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, + /* proto = */ nullptr, TenuredObject); } JS_PUBLIC_API(void*) diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 033b416afc..e7fc142ecd 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -10,6 +10,7 @@ #include "jsobj.h" #include "builtin/TypedObjectConstants.h" +#include "js/GCHashTable.h" #include "vm/Runtime.h" #include "vm/SharedMem.h" @@ -212,8 +213,10 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes, BufferContents contents, OwnsState ownsState = OwnsData, + HandleObject proto = nullptr, NewObjectKind newKind = GenericObject); static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes, + HandleObject proto = nullptr, NewObjectKind newKind = GenericObject); static JSObject* createSlice(JSContext* cx, Handle arrayBuffer, @@ -501,6 +504,12 @@ class InnerViewTable friend class ArrayBufferObject; private: + struct MapGCPolicy { + static bool needsSweep(JSObject** key, ViewVector* value) { + return InnerViewTable::sweepEntry(key, *value); + } + }; + // This key is a raw pointer and not a ReadBarriered because the post- // barrier would hold nursery-allocated entries live unconditionally. It is // a very common pattern in low-level and performance-oriented JavaScript @@ -509,10 +518,11 @@ class InnerViewTable // performance regression. Thus, it is vital that nursery pointers in this // map not be held live. Special support is required in the minor GC, // implemented in sweepAfterMinorGC. - typedef HashMap, - SystemAllocPolicy> Map; + typedef GCHashMap, + SystemAllocPolicy, + MapGCPolicy> Map; // For all objects sharing their storage with some other view, this maps // the object to the list of such views. All entries in this map are weak. @@ -529,7 +539,7 @@ class InnerViewTable bool nurseryKeysValid; // Sweep an entry during GC, returning whether the entry should be removed. - bool sweepEntry(JSObject** pkey, ViewVector& views); + static bool sweepEntry(JSObject** pkey, ViewVector& views); bool addView(JSContext* cx, ArrayBufferObject* obj, ArrayBufferViewObject* view); ViewVector* maybeViewsUnbarriered(ArrayBufferObject* obj); @@ -542,8 +552,8 @@ class InnerViewTable // Remove references to dead objects in the table and update table entries // to reflect moved objects. - void sweep(JSRuntime* rt); - void sweepAfterMinorGC(JSRuntime* rt); + void sweep(); + void sweepAfterMinorGC(); bool needsSweepAfterMinorGC() { return !nurseryKeys.empty() || !nurseryKeysValid; diff --git a/js/src/vm/BooleanObject-inl.h b/js/src/vm/BooleanObject-inl.h index c8be7646e4..c5e0f75381 100644 --- a/js/src/vm/BooleanObject-inl.h +++ b/js/src/vm/BooleanObject-inl.h @@ -14,14 +14,13 @@ namespace js { inline BooleanObject* -BooleanObject::create(JSContext* cx, bool b) +BooleanObject::create(JSContext* cx, bool b, HandleObject proto /* = nullptr */) { - JSObject* obj = NewBuiltinClassInstance(cx, &class_); + BooleanObject* obj = NewObjectWithClassProto(cx, proto); if (!obj) return nullptr; - BooleanObject& boolobj = obj->as(); - boolobj.setPrimitiveValue(b); - return &boolobj; + obj->setPrimitiveValue(b); + return obj; } } // namespace js diff --git a/js/src/vm/BooleanObject.h b/js/src/vm/BooleanObject.h index 5472c50303..c3c74fb198 100644 --- a/js/src/vm/BooleanObject.h +++ b/js/src/vm/BooleanObject.h @@ -24,10 +24,11 @@ class BooleanObject : public NativeObject static const Class class_; /* - * Creates a new Boolean object boxing the given primitive bool. The - * object's [[Prototype]] is determined from context. + * Creates a new Boolean object boxing the given primitive bool. + * If proto is nullptr, the [[Prototype]] will default to Boolean.prototype. */ - static inline BooleanObject* create(JSContext* cx, bool b); + static inline BooleanObject* create(JSContext* cx, bool b, + HandleObject proto = nullptr); bool unbox() const { return getFixedSlot(PRIMITIVE_VALUE_SLOT).toBoolean(); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 55805552a6..54924b0001 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -975,6 +975,26 @@ Debugger::unwrapPropertyDescriptor(JSContext* cx, HandleObject obj, return true; } +namespace { +class MOZ_STACK_CLASS ReportExceptionClosure : public ScriptEnvironmentPreparer::Closure +{ +public: + explicit ReportExceptionClosure(RootedValue& exn) + : exn_(exn) + { + } + + bool operator()(JSContext* cx) override + { + cx->setPendingException(exn_); + return false; + } + +private: + RootedValue& exn_; +}; +} // anonymous namespace + JSTrapStatus Debugger::handleUncaughtExceptionHelper(Maybe& ac, MutableHandleValue* vp, bool callHook) @@ -993,7 +1013,31 @@ Debugger::handleUncaughtExceptionHelper(Maybe& ac, } if (cx->isExceptionPending()) { - JS_ReportPendingException(cx); + /* + * We want to report the pending exception, but we want to let the + * embedding handle it however it wants to. So pretend like we're + * starting a new script execution on our current compartment (which + * is the debugger compartment, so reported errors won't get + * reported to various onerror handlers in debuggees) and as part of + * that "execution" simply throw our exception so the embedding can + * deal. + */ + RootedValue exn(cx); + if (cx->getPendingException(&exn)) { + /* + * Clear the exception, because + * PrepareScriptEnvironmentAndInvoke will assert that we don't + * have one. + */ + cx->clearPendingException(); + ReportExceptionClosure reportExn(exn); + PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn); + } + /* + * And if not, or if PrepareScriptEnvironmentAndInvoke somehow left + * an exception on cx (which it totally shouldn't do), just give + * up. + */ cx->clearPendingException(); } } diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp index 0b141f2689..cdf07f3a04 100644 --- a/js/src/vm/ErrorObject.cpp +++ b/js/src/vm/ErrorObject.cpp @@ -84,13 +84,17 @@ js::ErrorObject::init(JSContext* cx, Handle obj, JSExnType type, /* static */ ErrorObject* js::ErrorObject::create(JSContext* cx, JSExnType errorType, HandleObject stack, HandleString fileName, uint32_t lineNumber, uint32_t columnNumber, - ScopedJSFreePtr* report, HandleString message) + ScopedJSFreePtr* report, HandleString message, + HandleObject protoArg /* = nullptr */) { AssertObjectIsSavedFrameOrWrapper(cx, stack); - Rooted proto(cx, GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(), errorType)); - if (!proto) - return nullptr; + RootedObject proto(cx, protoArg); + if (!proto) { + proto = GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(), errorType); + if (!proto) + return nullptr; + } Rooted errObject(cx); { diff --git a/js/src/vm/ErrorObject.h b/js/src/vm/ErrorObject.h index c4edd40389..39e2c37f5d 100644 --- a/js/src/vm/ErrorObject.h +++ b/js/src/vm/ErrorObject.h @@ -72,7 +72,7 @@ class ErrorObject : public NativeObject static ErrorObject* create(JSContext* cx, JSExnType type, HandleObject stack, HandleString fileName, uint32_t lineNumber, uint32_t columnNumber, ScopedJSFreePtr* report, - HandleString message); + HandleString message, HandleObject proto = nullptr); /* * Assign the initial error shape to the empty object. (This shape does diff --git a/js/src/vm/ForOfIterator.cpp b/js/src/vm/ForOfIterator.cpp index 0563a085bb..590327306a 100644 --- a/js/src/vm/ForOfIterator.cpp +++ b/js/src/vm/ForOfIterator.cpp @@ -53,7 +53,7 @@ ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavio InvokeArgs args(cx); if (!args.init(0)) return false; - args.setThis(ObjectValue(*iterableObj)); + args.setThis(iterable); RootedValue callee(cx); RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); @@ -123,7 +123,6 @@ bool ForOfIterator::next(MutableHandleValue vp, bool* done) { MOZ_ASSERT(iterator); - if (index != NOT_ARRAY) { ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx_); if (!stubChain) @@ -143,11 +142,10 @@ ForOfIterator::next(MutableHandleValue vp, bool* done) return false; InvokeArgs args(cx_); - if (!args.init(1)) + if (!args.init(0)) return false; args.setCallee(method); args.setThis(ObjectValue(*iterator)); - args[0].setUndefined(); if (!Invoke(cx_, args)) return false; diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 3e96786665..b4d093e389 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -288,7 +288,7 @@ js::ReportIsNotFunction(JSContext* cx, HandleValue v, int numToSkip, MaybeConstr unsigned error = construct ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION; int spIndex = numToSkip >= 0 ? -(numToSkip + 1) : JSDVG_SEARCH_STACK; - ReportValueError3(cx, error, spIndex, v, nullptr, nullptr, nullptr); + ReportValueError(cx, error, spIndex, v, nullptr); return false; } @@ -1775,7 +1775,6 @@ CASE(JSOP_NOP) CASE(JSOP_UNUSED14) CASE(JSOP_UNUSED65) CASE(JSOP_BACKPATCH) -CASE(JSOP_UNUSED145) CASE(JSOP_UNUSED163) CASE(JSOP_UNUSED177) CASE(JSOP_UNUSED178) @@ -2774,6 +2773,7 @@ CASE(JSOP_FUNAPPLY) CASE(JSOP_NEW) CASE(JSOP_CALL) +CASE(JSOP_CALLITER) CASE(JSOP_SUPERCALL) CASE(JSOP_FUNCALL) { @@ -2797,6 +2797,11 @@ CASE(JSOP_FUNCALL) if (!ConstructFromStack(cx, args)) goto error; } else { + if (*REGS.pc == JSOP_CALLITER && args.calleev().isPrimitive()) { + MOZ_ASSERT(args.length() == 0, "thisv must be on top of the stack"); + ReportValueError(cx, JSMSG_NOT_ITERABLE, -1, args.thisv(), nullptr); + goto error; + } if (!Invoke(cx, args)) goto error; } @@ -4817,20 +4822,7 @@ js::DefaultClassConstructor(JSContext* cx, unsigned argc, Value* vp) } RootedObject newTarget(cx, &args.newTarget().toObject()); - RootedValue protoVal(cx); - - if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protoVal)) - return false; - - RootedObject proto(cx); - if (!protoVal.isObject()) { - if (!GetBuiltinPrototype(cx, JSProto_Object, &proto)) - return false; - } else { - proto = &protoVal.toObject(); - } - - JSObject* obj = NewObjectWithGivenProto(cx, &PlainObject::class_, proto); + JSObject* obj = CreateThis(cx, &PlainObject::class_, newTarget); if (!obj) return false; diff --git a/js/src/vm/NumberObject-inl.h b/js/src/vm/NumberObject-inl.h index 5d4ff02143..7e0237b1c8 100644 --- a/js/src/vm/NumberObject-inl.h +++ b/js/src/vm/NumberObject-inl.h @@ -14,14 +14,13 @@ namespace js { inline NumberObject* -NumberObject::create(JSContext* cx, double d) +NumberObject::create(JSContext* cx, double d, HandleObject proto /* = nullptr */) { - JSObject* obj = NewBuiltinClassInstance(cx, &class_); + NumberObject* obj = NewObjectWithClassProto(cx, proto); if (!obj) return nullptr; - NumberObject& numobj = obj->as(); - numobj.setPrimitiveValue(d); - return &numobj; + obj->setPrimitiveValue(d); + return obj; } } // namespace js diff --git a/js/src/vm/NumberObject.h b/js/src/vm/NumberObject.h index c3523b8465..dd808309c7 100644 --- a/js/src/vm/NumberObject.h +++ b/js/src/vm/NumberObject.h @@ -22,10 +22,11 @@ class NumberObject : public NativeObject static const Class class_; /* - * Creates a new Number object boxing the given number. The object's - * [[Prototype]] is determined from context. + * Creates a new Number object boxing the given number. + * If proto is nullptr, then Number.prototype will be used instead. */ - static inline NumberObject* create(JSContext* cx, double d); + static inline NumberObject* create(JSContext* cx, double d, + HandleObject proto = nullptr); double unbox() const { return getFixedSlot(PRIMITIVE_VALUE_SLOT).toNumber(); diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 031a3b2833..4bc6eb3d82 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -1333,40 +1333,69 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_ // ObjectGroupCompartment AllocationSiteTable ///////////////////////////////////////////////////////////////////// -struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher { - JSScript* script; +struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher, + public JS::Traceable { + ReadBarrieredScript script; uint32_t offset : 24; JSProtoKey kind : 8; + ReadBarrieredObject proto; + static const uint32_t OFFSET_LIMIT = (1 << 23); - AllocationSiteKey() { mozilla::PodZero(this); } + AllocationSiteKey(JSScript* script_, uint32_t offset_, JSProtoKey kind_, JSObject* proto_) + : script(script_), offset(offset_), kind(kind_), proto(proto_) + { + MOZ_ASSERT(offset_ < OFFSET_LIMIT); + } + + AllocationSiteKey(AllocationSiteKey&& key) + : script(mozilla::Move(key.script)), + offset(key.offset), + kind(key.kind), + proto(mozilla::Move(key.proto)) + { } + + AllocationSiteKey(const AllocationSiteKey& key) + : script(key.script), + offset(key.offset), + kind(key.kind), + proto(key.proto) + { } static inline uint32_t hash(AllocationSiteKey key) { - return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); + return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind ^ + MovableCellHasher::hash(key.proto)); } static inline bool match(const AllocationSiteKey& a, const AllocationSiteKey& b) { - return a.script == b.script && a.offset == b.offset && a.kind == b.kind; + return DefaultHasher::match(a.script, b.script) && + a.offset == b.offset && + a.kind == b.kind && + MovableCellHasher::match(a.proto, b.proto); + } + + static void trace(AllocationSiteKey* key, JSTracer* trc) { + TraceRoot(trc, &key->script, "AllocationSiteKey script"); + TraceNullableRoot(trc, &key->proto, "AllocationSiteKey proto"); } }; /* static */ ObjectGroup* -ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc, - JSProtoKey kind) +ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode* pc, + JSProtoKey kind, HandleObject protoArg /* = nullptr */) { - MOZ_ASSERT(!useSingletonForAllocationSite(script, pc, kind)); + MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind)); + MOZ_ASSERT_IF(protoArg, kind == JSProto_Array); - uint32_t offset = script->pcToOffset(pc); + uint32_t offset = scriptArg->pcToOffset(pc); - if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) + if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) { + if (protoArg) + return defaultNewGroup(cx, GetClassForProtoKey(kind), TaggedProto(protoArg)); return defaultNewGroup(cx, kind); - - ObjectGroupCompartment::AllocationSiteKey key; - key.script = script; - key.offset = offset; - key.kind = kind; + } ObjectGroupCompartment::AllocationSiteTable*& table = cx->compartment()->objectGroups.allocationSiteTable; @@ -1381,16 +1410,20 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc } } + RootedScript script(cx, scriptArg); + RootedObject proto(cx, protoArg); + if (!proto && kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto)) + return nullptr; + + Rooted key(cx, + ObjectGroupCompartment::AllocationSiteKey(script, offset, kind, proto)); + ObjectGroupCompartment::AllocationSiteTable::AddPtr p = table->lookupForAdd(key); if (p) return p->value(); AutoEnterAnalysis enter(cx); - RootedObject proto(cx); - if (kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto)) - return nullptr; - Rooted tagged(cx, TaggedProto(proto)); ObjectGroup* res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE); @@ -1435,10 +1468,7 @@ void ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc, JSProtoKey kind, ObjectGroup* group) { - AllocationSiteKey key; - key.script = script; - key.offset = script->pcToOffset(pc); - key.kind = kind; + AllocationSiteKey key(script, script->pcToOffset(pc), kind, group->proto().toObjectOrNull()); AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key); MOZ_RELEASE_ASSERT(p); @@ -1451,12 +1481,16 @@ ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* } /* static */ ObjectGroup* -ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key) +ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key, HandleObject proto) { + MOZ_ASSERT_IF(proto, key == JSProto_Array); + jsbytecode* pc; RootedScript script(cx, cx->currentScript(&pc)); if (script) - return allocationSiteGroup(cx, script, pc, key); + return allocationSiteGroup(cx, script, pc, key, proto); + if (proto) + return defaultNewGroup(cx, GetClassForProtoKey(key), TaggedProto(proto)); return defaultNewGroup(cx, key); } @@ -1741,13 +1775,11 @@ ObjectGroupCompartment::sweep(FreeOp* fop) if (allocationSiteTable) { for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { - AllocationSiteKey key = e.front().key(); - bool keyDying = IsAboutToBeFinalizedUnbarriered(&key.script); + bool keyDying = IsAboutToBeFinalized(&e.front().mutableKey().script) || + (e.front().key().proto && IsAboutToBeFinalized(&e.front().mutableKey().proto)); bool valDying = IsAboutToBeFinalized(&e.front().value()); if (keyDying || valDying) e.removeFront(); - else if (key.script != e.front().key().script) - e.rekeyFront(key); } } diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index a83fe0071e..a1b09dc356 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -510,10 +510,11 @@ class ObjectGroup : public gc::TenuredCell // Get a non-singleton group to use for objects created at the specified // allocation site. static ObjectGroup* allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc, - JSProtoKey key); + JSProtoKey key, HandleObject proto = nullptr); // Get a non-singleton group to use for objects created in a JSNative call. - static ObjectGroup* callingAllocationSiteGroup(JSContext* cx, JSProtoKey key); + static ObjectGroup* callingAllocationSiteGroup(JSContext* cx, JSProtoKey key, + HandleObject proto = nullptr); // Set the group or singleton-ness of an object created for an allocation site. static bool diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 2abe80f57a..4cdf366a0c 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1474,7 +1474,16 @@ * Stack: val => val */ \ macro(JSOP_SETINTRINSIC, 144, "setintrinsic", NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \ - macro(JSOP_UNUSED145, 145, "unused145", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Like JSOP_CALL, but used as part of for-of and destructuring bytecode + * to provide better error messages. + * Category: Statements + * Type: Function + * Operands: uint16_t argc (must be 0) + * Stack: callee, this => rval + * nuses: 2 + */ \ + macro(JSOP_CALLITER, 145, "calliter", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ /* * Initialize a non-configurable, non-writable, non-enumerable data-property on an object. * diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 88b8b3143d..89b3bf6ac3 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -41,25 +41,17 @@ JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE); JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY); RegExpObject* -js::RegExpAlloc(ExclusiveContext* cx) +js::RegExpAlloc(ExclusiveContext* cx, HandleObject proto /* = nullptr */) { // Note: RegExp objects are always allocated in the tenured heap. This is // not strictly required, but simplifies embedding them in jitcode. - RegExpObject* regexp = NewBuiltinClassInstance(cx, TenuredObject); + RegExpObject* regexp = NewObjectWithClassProto(cx, proto, TenuredObject); if (!regexp) return nullptr; - regexp->initPrivate(nullptr); return regexp; } -RegExpObject* -js::InitializeRegExp(ExclusiveContext* cx, Handle regexp, HandleAtom source, - RegExpFlag flags) -{ - return regexp->init(cx, source, flags) ? regexp : nullptr; -} - /* MatchPairs */ bool @@ -155,6 +147,13 @@ RegExpObject::trace(JSTracer* trc, JSObject* obj) } } +/* static */ bool +RegExpObject::initFromAtom(ExclusiveContext* cx, Handle regexp, HandleAtom source, + RegExpFlag flags) +{ + return regexp->init(cx, source, flags); +} + const Class RegExpObject::class_ = { js_RegExp_str, JSCLASS_HAS_PRIVATE | @@ -224,7 +223,10 @@ RegExpObject::createNoStatics(ExclusiveContext* cx, HandleAtom source, RegExpFla if (!regexp) return nullptr; - return InitializeRegExp(cx, regexp, source, flags); + if (!RegExpObject::initFromAtom(cx, regexp, source, flags)) + return nullptr; + + return regexp; } bool @@ -894,7 +896,10 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_) if (!clone) return nullptr; - return InitializeRegExp(cx, clone, source, RegExpFlag(origFlags | staticsFlags)); + if (!RegExpObject::initFromAtom(cx, clone, source, RegExpFlag(origFlags | staticsFlags))) + return nullptr; + + return clone; } // Otherwise, the clone can use |regexp|'s RegExpShared. @@ -911,7 +916,7 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_) if (!regex->getShared(cx, &g)) return nullptr; - if (!InitializeRegExp(cx, clone, source, g->getFlags())) + if (!RegExpObject::initFromAtom(cx, clone, source, g->getFlags())) return nullptr; clone->setShared(*g.re()); diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 02972e3143..ad118914d9 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -64,11 +64,7 @@ enum RegExpRunStatus }; extern RegExpObject* -RegExpAlloc(ExclusiveContext* cx); - -extern RegExpObject* -InitializeRegExp(ExclusiveContext* cx, Handle regexp, HandleAtom source, - RegExpFlag flags); +RegExpAlloc(ExclusiveContext* cx, HandleObject proto = nullptr); // |regexp| is under-typed because this function's used in the JIT. extern JSObject* @@ -447,11 +443,10 @@ class RegExpObject : public NativeObject static void trace(JSTracer* trc, JSObject* obj); - private: - friend RegExpObject* - InitializeRegExp(ExclusiveContext* cx, Handle regexp, HandleAtom source, - RegExpFlag flags); + static bool initFromAtom(ExclusiveContext* cx, Handle regexp, HandleAtom source, + RegExpFlag flags); + private: bool init(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags); /* diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 3c47fc7435..917699ac8b 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1263,10 +1263,21 @@ intrinsic_ConstructorForTypedArray(JSContext* cx, unsigned argc, Value* vp) RootedObject object(cx, &args[0].toObject()); JSProtoKey protoKey = StandardProtoKeyOrNull(object); MOZ_ASSERT(protoKey); - RootedValue ctor(cx, cx->global()->getConstructor(protoKey)); - MOZ_ASSERT(ctor.isObject()); - args.rval().set(ctor); + // While it may seem like an invariant that in any compartment, + // seeing a typed array object implies that the TypedArray constructor + // for that type is initialized on the compartment's global, this is not + // the case. When we construct a typed array given a cross-compartment + // ArrayBuffer, we put the constructed TypedArray in the same compartment + // as the ArrayBuffer. Since we use the prototype from the initial + // compartment, and never call the constructor in the ArrayBuffer's + // compartment from script, we are not guaranteed to have initialized + // the constructor. + RootedObject ctor(cx); + if (!GetBuiltinConstructor(cx, protoKey, &ctor)) + return false; + + args.rval().setObject(*ctor); return true; } diff --git a/js/src/vm/StringObject-inl.h b/js/src/vm/StringObject-inl.h index de3a9714f7..5fc1656f69 100644 --- a/js/src/vm/StringObject-inl.h +++ b/js/src/vm/StringObject-inl.h @@ -33,9 +33,9 @@ StringObject::init(JSContext* cx, HandleString str) } inline StringObject* -StringObject::create(JSContext* cx, HandleString str, NewObjectKind newKind) +StringObject::create(JSContext* cx, HandleString str, HandleObject proto, NewObjectKind newKind) { - JSObject* obj = NewBuiltinClassInstance(cx, &class_, newKind); + JSObject* obj = NewObjectWithClassProto(cx, &class_, proto, newKind); if (!obj) return nullptr; Rooted strobj(cx, &obj->as()); diff --git a/js/src/vm/StringObject.h b/js/src/vm/StringObject.h index 57adbc80a5..119e3d9fa6 100644 --- a/js/src/vm/StringObject.h +++ b/js/src/vm/StringObject.h @@ -29,6 +29,7 @@ class StringObject : public NativeObject * [[Prototype]] is determined from context. */ static inline StringObject* create(JSContext* cx, HandleString str, + HandleObject proto = nullptr, NewObjectKind newKind = GenericObject); /* diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index cd1128ef45..393e0d64f7 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -111,6 +111,12 @@ AnyTypedArrayByteLength(const JSObject* obj) return obj->as().byteLength(); } +inline bool +AnyTypedArrayIsDetached(const JSObject* obj) +{ + return obj->as().isNeutered(); +} + inline bool IsAnyTypedArrayClass(const Class* clasp) { diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index a0c4e71ace..8a411a76c7 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -180,6 +180,20 @@ NewArray(JSContext* cx, uint32_t nelements); namespace { +// We allow nullptr for newTarget for all the creation methods, to allow for +// JSFriendAPI functions that don't care about subclassing +static bool +GetPrototypeForInstance(JSContext* cx, HandleObject newTarget, MutableHandleObject proto) +{ + if (newTarget) { + if (!GetPrototypeFromConstructor(cx, newTarget, proto)) + return false; + } else { + proto.set(nullptr); + } + return true; +} + template class TypedArrayObjectTemplate : public TypedArrayObject { @@ -282,17 +296,8 @@ class TypedArrayObjectTemplate : public TypedArrayObject { MOZ_ASSERT(proto); - RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind)); - if (!obj) - return nullptr; - - ObjectGroup* group = ObjectGroup::defaultNewGroup(cx, obj->getClass(), - TaggedProto(proto.get())); - if (!group) - return nullptr; - obj->setGroup(group); - - return &obj->as(); + JSObject* obj = NewObjectWithClassProto(cx, instanceClass(), proto, allocKind); + return obj ? &obj->as() : nullptr; } static TypedArrayObject* @@ -334,9 +339,16 @@ class TypedArrayObjectTemplate : public TypedArrayObject ? GetGCObjectKind(instanceClass()) : AllocKindForLazyBuffer(len * sizeof(NativeType)); + // Subclassing mandates that we hand in the proto every time. Most of + // the time, though, that [[Prototype]] will not be interesting. If + // it isn't, we can do some more TI optimizations. + RootedObject checkProto(cx); + if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &checkProto)) + return nullptr; + AutoSetNewObjectMetadata metadata(cx); Rooted obj(cx); - if (proto) + if (proto && proto != checkProto) obj = makeProtoInstance(cx, proto, allocKind); else obj = makeTypedInstance(cx, len, allocKind); @@ -429,10 +441,13 @@ class TypedArrayObjectTemplate : public TypedArrayObject static JSObject* create(JSContext* cx, const CallArgs& args) { + MOZ_ASSERT(args.isConstructing()); + RootedObject newTarget(cx, &args.newTarget().toObject()); + /* () or (number) */ uint32_t len = 0; if (args.length() == 0 || ValueIsLength(args[0], &len)) - return fromLength(cx, len); + return fromLength(cx, len, newTarget); /* (not an object) */ if (!args[0].isObject()) { @@ -453,9 +468,13 @@ class TypedArrayObjectTemplate : public TypedArrayObject * shared array's values are copied here. */ if (!UncheckedUnwrap(dataObj)->is()) - return fromArray(cx, dataObj); + return fromArray(cx, dataObj, newTarget); /* (ArrayBuffer, [byteOffset, [length]]) */ + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return nullptr; + int32_t byteOffset = 0; int32_t length = -1; @@ -479,7 +498,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject } } - return fromBuffer(cx, dataObj, byteOffset, length); + return fromBufferWithProto(cx, dataObj, byteOffset, length, proto); } public: @@ -533,9 +552,11 @@ class TypedArrayObjectTemplate : public TypedArrayObject * don't have to do anything *uniquely* crazy here. */ - Rooted proto(cx); - if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &proto)) - return nullptr; + RootedObject protoRoot(cx, proto); + if (!protoRoot) { + if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &protoRoot)) + return nullptr; + } InvokeArgs args(cx); if (!args.init(3)) @@ -545,7 +566,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject args.setThis(ObjectValue(*bufobj)); args[0].setNumber(byteOffset); args[1].setInt32(lengthInt); - args[2].setObject(*proto); + args[2].setObject(*protoRoot); if (!Invoke(cx, args)) return nullptr; @@ -559,10 +580,17 @@ class TypedArrayObjectTemplate : public TypedArrayObject } Rooted buffer(cx); - if (IsArrayBuffer(bufobj)) - buffer = static_cast(&AsArrayBuffer(bufobj)); - else + if (IsArrayBuffer(bufobj)) { + ArrayBufferObject& buf = AsArrayBuffer(bufobj); + if (buf.isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return nullptr; + } + + buffer = static_cast(&buf); + } else { buffer = static_cast(&AsSharedArrayBuffer(bufobj)); + } if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); @@ -622,16 +650,21 @@ class TypedArrayObjectTemplate : public TypedArrayObject } static JSObject* - fromLength(JSContext* cx, uint32_t nelements) + fromLength(JSContext* cx, uint32_t nelements, HandleObject newTarget = nullptr) { + RootedObject proto(cx); + if (!GetPrototypeForInstance(cx, newTarget, &proto)) + return nullptr; + Rooted buffer(cx); if (!maybeCreateArrayBuffer(cx, nelements, &buffer)) return nullptr; - return makeInstance(cx, buffer, 0, nelements); + + return makeInstance(cx, buffer, 0, nelements, proto); } static JSObject* - fromArray(JSContext* cx, HandleObject other); + fromArray(JSContext* cx, HandleObject other, HandleObject newTarget = nullptr); static const NativeType getIndex(JSObject* obj, uint32_t index) @@ -671,20 +704,35 @@ struct TypedArrayObject::OfType template /* static */ JSObject* -TypedArrayObjectTemplate::fromArray(JSContext* cx, HandleObject other) +TypedArrayObjectTemplate::fromArray(JSContext* cx, HandleObject other, + HandleObject newTarget /* = nullptr */) { + // Allow nullptr newTarget for FriendAPI methods, which don't care about + // subclassing. + RootedObject proto(cx); + uint32_t len; if (IsAnyTypedArray(other)) { + if (!GetPrototypeForInstance(cx, newTarget, &proto)) + return nullptr; + + if (AnyTypedArrayIsDetached(other)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return nullptr; + } len = AnyTypedArrayLength(other); - } else if (!GetLengthProperty(cx, other, &len)) { - return nullptr; + } else { + if (!GetLengthProperty(cx, other, &len)) + return nullptr; + if (!GetPrototypeForInstance(cx, newTarget, &proto)) + return nullptr; } Rooted buffer(cx); if (!maybeCreateArrayBuffer(cx, len, &buffer)) return nullptr; - Rooted obj(cx, makeInstance(cx, buffer, 0, len)); + Rooted obj(cx, makeInstance(cx, buffer, 0, len, proto)); if (!obj || !TypedArrayMethods::setFromArrayLike(cx, obj, other, len)) return nullptr; return obj; @@ -970,7 +1018,7 @@ DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto) return GenericObject; } -inline DataViewObject* +DataViewObject* DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, Handle arrayBuffer, JSObject* protoArg) { @@ -988,24 +1036,21 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, RootedObject obj(cx); NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto); - obj = NewBuiltinClassInstance(cx, &class_, newKind); + obj = NewObjectWithClassProto(cx, &class_, proto, newKind); if (!obj) return nullptr; - if (proto) { - ObjectGroup* group = ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(proto)); - if (!group) - return nullptr; - obj->setGroup(group); - } else if (byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) { - MOZ_ASSERT(obj->isSingleton()); - } else { - jsbytecode* pc; - RootedScript script(cx, cx->currentScript(&pc)); - if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, - newKind == SingletonObject)) - { - return nullptr; + if (!proto) { + if (byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) { + MOZ_ASSERT(obj->isSingleton()); + } else { + jsbytecode* pc; + RootedScript script(cx, cx->currentScript(&pc)); + if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, + newKind == SingletonObject)) + { + return nullptr; + } } } @@ -1036,7 +1081,8 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, } bool -DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, HandleObject proto) +DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args, + uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr) { if (!IsArrayBuffer(bufobj)) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, @@ -1052,29 +1098,40 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, if (!ToUint32(cx, args[1], &byteOffset)) return false; if (byteOffset > INT32_MAX) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, - JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); + return false; + } + } + + if (buffer->isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + + if (args.length() > 1) { + if (byteOffset > byteLength) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); return false; } - if (!args.get(2).isUndefined()) { + if (args.get(2).isUndefined()) { + byteLength -= byteOffset; + } else { if (!ToUint32(cx, args[2], &byteLength)) return false; if (byteLength > INT32_MAX) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, - JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); + JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); return false; } - } else { - uint32_t bufferLength = buffer->byteLength(); - if (byteOffset > bufferLength) { + MOZ_ASSERT(byteOffset + byteLength >= byteOffset, + "can't overflow: both numbers are less than INT32_MAX"); + if (byteOffset + byteLength > buffer->byteLength()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); return false; } - - byteLength = bufferLength - byteOffset; } } @@ -1082,11 +1139,29 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, MOZ_ASSERT(byteOffset <= INT32_MAX); MOZ_ASSERT(byteLength <= INT32_MAX); - if (byteOffset + byteLength > buffer->byteLength()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); - return false; - } + *byteOffsetPtr = byteOffset; + *byteLengthPtr = byteLength; + + return true; +} + +bool +DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args) +{ + MOZ_ASSERT(args.isConstructing()); + assertSameCompartment(cx, bufobj); + + uint32_t byteOffset, byteLength; + if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength)) + return false; + + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted buffer(cx, &AsArrayBuffer(bufobj)); JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto); if (!obj) return false; @@ -1094,6 +1169,70 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, return true; } +// Create a DataView object in another compartment. +// +// ES6 supports creating a DataView in global A (using global A's DataView +// constructor) backed by an ArrayBuffer created in global B. +// +// Our DataViewObject implementation doesn't support a DataView in +// compartment A backed by an ArrayBuffer in compartment B. So in this case, +// we create the DataView in B (!) and return a cross-compartment wrapper. +// +// Extra twist: the spec says the new DataView's [[Prototype]] must be +// A's DataView.prototype. So even though we're creating the DataView in B, +// its [[Prototype]] must be (a cross-compartment wrapper for) the +// DataView.prototype in A. +// +// As if this were not confusing enough, the way we actually do this is also +// tricky. We call compartment A's createDataViewForThis method, passing it +// bufobj as `this`. That calls ArrayBufferObject::createDataViewForThis(), +// which uses CallNonGenericMethod to switch to compartment B so that +// the new DataView is created there. +bool +DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args) +{ + MOZ_ASSERT(args.isConstructing()); + MOZ_ASSERT(bufobj->is()); + + JSObject* unwrapped = CheckedUnwrap(bufobj); + if (!unwrapped) { + JS_ReportError(cx, "Permission denied to access object"); + return false; + } + + // NB: This entails the IsArrayBuffer check + uint32_t byteOffset, byteLength; + if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength)) + return false; + + // Make sure to get the [[Prototype]] for the created view from this + // compartment. + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted global(cx, cx->compartment()->maybeGlobal()); + if (!proto) { + proto = global->getOrCreateDataViewPrototype(cx); + if (!proto) + return false; + } + + InvokeArgs args2(cx); + if (!args2.init(3)) + return false; + args2.setCallee(global->createDataViewForThis()); + args2.setThis(ObjectValue(*bufobj)); + args2[0].set(PrivateUint32Value(byteOffset)); + args2[1].set(PrivateUint32Value(byteLength)); + args2[2].setObject(*proto); + if (!Invoke(cx, args2)) + return false; + args.rval().set(args2.rval()); + return true; +} + bool DataViewObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) { @@ -1106,26 +1245,9 @@ DataViewObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj)) return false; - if (bufobj->is() && IsArrayBuffer(UncheckedUnwrap(bufobj))) { - Rooted global(cx, cx->compartment()->maybeGlobal()); - Rooted proto(cx, global->getOrCreateDataViewPrototype(cx)); - if (!proto) - return false; - - InvokeArgs args2(cx); - if (!args2.init(args.length() + 1)) - return false; - args2.setCallee(global->createDataViewForThis()); - args2.setThis(ObjectValue(*bufobj)); - PodCopy(args2.array(), args.array(), args.length()); - args2[args.length()].setObject(*proto); - if (!Invoke(cx, args2)) - return false; - args.rval().set(args2.rval()); - return true; - } - - return construct(cx, bufobj, args, nullptr); + if (bufobj->is()) + return constructWrapped(cx, bufobj, args); + return constructSameCompartment(cx, bufobj, args); } template @@ -1232,6 +1354,11 @@ DataViewObject::read(JSContext* cx, Handle obj, bool fromLittleEndian = args.length() >= 2 && ToBoolean(args[1]); + if (obj->arrayBuffer().isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + uint8_t* data = DataViewObject::getDataPointer(cx, obj, offset); if (!data) return false; @@ -1293,6 +1420,11 @@ DataViewObject::write(JSContext* cx, Handle obj, bool toLittleEndian = args.length() >= 3 && ToBoolean(args[2]); + if (obj->arrayBuffer().isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + uint8_t* data = DataViewObject::getDataPointer(cx, obj, offset); if (!data) return false; @@ -1713,15 +1845,15 @@ TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) */ #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \ - JS_FRIEND_API(JSObject*) JS_New ## Name ## Array(JSContext* cx, uint32_t nelements) \ + JS_FRIEND_API(JSObject*) JS_New ## Name ## Array(JSContext* cx, uint32_t nelements) \ { \ return TypedArrayObjectTemplate::fromLength(cx, nelements); \ } \ - JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayFromArray(JSContext* cx, HandleObject other) \ + JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayFromArray(JSContext* cx, HandleObject other) \ { \ return TypedArrayObjectTemplate::fromArray(cx, other); \ } \ - JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayWithBuffer(JSContext* cx, \ + JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayWithBuffer(JSContext* cx, \ HandleObject arrayBuffer, uint32_t byteOffset, int32_t length) \ { \ return TypedArrayObjectTemplate::fromBuffer(cx, arrayBuffer, byteOffset, \ @@ -1733,8 +1865,8 @@ TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) return false; \ const Class* clasp = obj->getClass(); \ return clasp == TypedArrayObjectTemplate::instanceClass(); \ - } \ - JS_FRIEND_API(JSObject*) js::Unwrap ## Name ## Array(JSObject* obj) \ + } \ + JS_FRIEND_API(JSObject*) js::Unwrap ## Name ## Array(JSObject* obj) \ { \ obj = CheckedUnwrap(obj); \ if (!obj) \ @@ -1743,7 +1875,7 @@ TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) if (clasp == TypedArrayObjectTemplate::instanceClass()) \ return obj; \ return nullptr; \ - } \ + } \ const js::Class* const js::detail::Name ## ArrayClassPtr = \ &js::TypedArrayObject::classes[TypedArrayObjectTemplate::ArrayTypeID()]; diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 9fe812cd9a..018060140b 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -198,7 +198,7 @@ class TypedArrayObject : public NativeObject } bool isNeutered() const { - return !isSharedMemory() && (!bufferUnshared() || bufferUnshared()->isNeutered()); + return !isSharedMemory() && bufferUnshared() && bufferUnshared()->isNeutered(); } private: @@ -384,6 +384,16 @@ class DataViewObject : public NativeObject static bool defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto); + static bool getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args, + uint32_t *byteOffset, uint32_t* byteLength); + static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args); + static bool constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args); + + friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args); + static DataViewObject* + create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, + Handle arrayBuffer, JSObject* proto); + public: static const Class class_; @@ -420,13 +430,6 @@ class DataViewObject : public NativeObject } static bool class_constructor(JSContext* cx, unsigned argc, Value* vp); - static bool constructWithProto(JSContext* cx, unsigned argc, Value* vp); - static bool construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, - HandleObject proto); - - static inline DataViewObject* - create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, - Handle arrayBuffer, JSObject* proto); static bool getInt8Impl(JSContext* cx, const CallArgs& args); static bool fun_getInt8(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 0b40a5c782..1961ec82af 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -1919,7 +1919,7 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, return true; #endif } else { - if (jit::js_JitOptions.disableUnboxedObjects) + if (jit::JitOptions.disableUnboxedObjects) return true; } diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index b71e6df380..1aafeb1ce6 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,7 +29,7 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 313; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 315; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 9e80782a85..0fa587064f 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1320,16 +1320,25 @@ xpc::SimulateActivityCallback(bool aActive) XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive); } -// static -bool +void XPCJSRuntime::EnvironmentPreparer::invoke(HandleObject scope, js::ScriptEnvironmentPreparer::Closure& closure) { MOZ_ASSERT(NS_IsMainThread()); nsIGlobalObject* global = NativeGlobal(scope); - NS_ENSURE_TRUE(global && global->GetGlobalJSObject(), false); + + // Not much we can do if we simply don't have a usable global here... + NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject()); AutoEntryScript aes(global, "JS-engine-initiated execution"); aes.TakeOwnershipOfErrorReporting(); - return closure(aes.cx()); + + MOZ_ASSERT(!JS_IsExceptionPending(aes.cx())); + + DebugOnly ok = closure(aes.cx()); + + MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aes.cx())); + + // The AutoEntryScript will check for JS_IsExceptionPending on the + // JSContext and report it as needed as it comes off the stack. } // static diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 47257c361b..1050f071f2 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -598,7 +598,7 @@ public: void RemoveGCCallback(xpcGCCallback cb); struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer { - bool invoke(JS::HandleObject scope, Closure& closure) override; + void invoke(JS::HandleObject scope, Closure& closure) override; }; EnvironmentPreparer mEnvironmentPreparer; diff --git a/js/xpconnect/tests/chrome/chrome.ini b/js/xpconnect/tests/chrome/chrome.ini index c661a82b5a..7d081f8558 100644 --- a/js/xpconnect/tests/chrome/chrome.ini +++ b/js/xpconnect/tests/chrome/chrome.ini @@ -108,3 +108,4 @@ skip-if = buildapp == 'mulet' skip-if = true [test_watchpoints.xul] [test_nsScriptErrorWithStack.html] +[test_windowProxyDeadWrapper.html] diff --git a/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html b/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html new file mode 100644 index 0000000000..91f4037f70 --- /dev/null +++ b/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html @@ -0,0 +1,72 @@ + + + + + + Test for Bug 1223372 + + + + + + +Mozilla Bug 1223372 + + + + + diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index b6d37c26af..15cc149b9b 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -162,6 +162,10 @@ WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope, // ToWindowProxyIfWindow can return a CCW if |obj| was a // navigated-away-from Window. Strip any CCWs. obj = js::UncheckedUnwrap(obj); + if (JS_IsDeadWrapper(obj)) { + JS_ReportError(cx, "Can't wrap dead object"); + return nullptr; + } MOZ_ASSERT(js::IsWindowProxy(obj)); } diff --git a/layout/svg/nsSVGUtils.h b/layout/svg/nsSVGUtils.h index befeeb4a48..d0d64472db 100644 --- a/layout/svg/nsSVGUtils.h +++ b/layout/svg/nsSVGUtils.h @@ -7,7 +7,6 @@ #define NS_SVGUTILS_H // include math.h to pick up definition of M_ maths defines e.g. M_PI -#define _USE_MATH_DEFINES #include #include "DrawMode.h" @@ -25,7 +24,6 @@ #include "nsISupportsBase.h" #include "nsMathUtils.h" #include "nsStyleStruct.h" -#include "mozilla/Constants.h" #include class gfxContext; diff --git a/media/libav/config_darwin.asm b/media/libav/config_darwin.asm index 5ad7e45f97..89413c594e 100644 --- a/media/libav/config_darwin.asm +++ b/media/libav/config_darwin.asm @@ -128,7 +128,6 @@ %define HAVE_MACH_MACH_TIME_H 1 %define HAVE_MACHINE_IOCTL_BT848_H 0 %define HAVE_MACHINE_IOCTL_METEOR_H 0 -%define HAVE_MALLOC_H 0 %define HAVE_POLL_H 1 %define HAVE_SNDIO_H 0 %define HAVE_SOUNDCARD_H 0 @@ -190,15 +189,12 @@ %define HAVE_INET_ATON 0 %define HAVE_ISATTY 1 %define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -%define HAVE_LOCALTIME_R 1 %define HAVE_MACH_ABSOLUTE_TIME 1 %define HAVE_MAPVIEWOFFILE 0 -%define HAVE_MEMALIGN 0 %define HAVE_MKSTEMP 1 %define HAVE_MMAP 1 %define HAVE_MPROTECT 1 %define HAVE_NANOSLEEP 1 -%define HAVE_POSIX_MEMALIGN 1 %define HAVE_SCHED_GETAFFINITY 0 %define HAVE_SETCONSOLETEXTATTRIBUTE 0 %define HAVE_SETMODE 0 diff --git a/media/libav/config_darwin.h b/media/libav/config_darwin.h index 55df762a13..ae7844f3aa 100644 --- a/media/libav/config_darwin.h +++ b/media/libav/config_darwin.h @@ -138,7 +138,6 @@ #define HAVE_MACH_MACH_TIME_H 1 #define HAVE_MACHINE_IOCTL_BT848_H 0 #define HAVE_MACHINE_IOCTL_METEOR_H 0 -#define HAVE_MALLOC_H 0 #define HAVE_POLL_H 1 #define HAVE_SNDIO_H 0 #define HAVE_SOUNDCARD_H 0 @@ -200,15 +199,12 @@ #define HAVE_INET_ATON 0 #define HAVE_ISATTY 1 #define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -#define HAVE_LOCALTIME_R 1 #define HAVE_MACH_ABSOLUTE_TIME 1 #define HAVE_MAPVIEWOFFILE 0 -#define HAVE_MEMALIGN 0 #define HAVE_MKSTEMP 1 #define HAVE_MMAP 1 #define HAVE_MPROTECT 1 #define HAVE_NANOSLEEP 1 -#define HAVE_POSIX_MEMALIGN 1 #define HAVE_SCHED_GETAFFINITY 0 #define HAVE_SETCONSOLETEXTATTRIBUTE 0 #define HAVE_SETMODE 0 diff --git a/media/libav/config_unix.asm b/media/libav/config_unix.asm index 7f16ba817b..c08045a0b4 100644 --- a/media/libav/config_unix.asm +++ b/media/libav/config_unix.asm @@ -134,7 +134,6 @@ %define HAVE_MACH_MACH_TIME_H 0 %define HAVE_MACHINE_IOCTL_BT848_H 0 %define HAVE_MACHINE_IOCTL_METEOR_H 0 -%define HAVE_MALLOC_H 1 %define HAVE_POLL_H 1 %define HAVE_SNDIO_H 0 %define HAVE_SOUNDCARD_H 0 @@ -196,15 +195,12 @@ %define HAVE_INET_ATON 0 %define HAVE_ISATTY 1 %define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -%define HAVE_LOCALTIME_R 1 %define HAVE_MACH_ABSOLUTE_TIME 0 %define HAVE_MAPVIEWOFFILE 0 -%define HAVE_MEMALIGN 1 %define HAVE_MKSTEMP 1 %define HAVE_MMAP 1 %define HAVE_MPROTECT 1 %define HAVE_NANOSLEEP 1 -%define HAVE_POSIX_MEMALIGN 1 %define HAVE_SCHED_GETAFFINITY 1 %define HAVE_SETCONSOLETEXTATTRIBUTE 0 %define HAVE_SETMODE 0 diff --git a/media/libav/config_unix.h b/media/libav/config_unix.h index 668a02ef2c..b3c44d62b7 100644 --- a/media/libav/config_unix.h +++ b/media/libav/config_unix.h @@ -144,7 +144,6 @@ #define HAVE_MACH_MACH_TIME_H 0 #define HAVE_MACHINE_IOCTL_BT848_H 0 #define HAVE_MACHINE_IOCTL_METEOR_H 0 -#define HAVE_MALLOC_H 1 #define HAVE_POLL_H 1 #define HAVE_SNDIO_H 0 #define HAVE_SOUNDCARD_H 0 @@ -206,15 +205,12 @@ #define HAVE_INET_ATON 0 #define HAVE_ISATTY 1 #define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -#define HAVE_LOCALTIME_R 1 #define HAVE_MACH_ABSOLUTE_TIME 0 #define HAVE_MAPVIEWOFFILE 0 -#define HAVE_MEMALIGN 1 #define HAVE_MKSTEMP 1 #define HAVE_MMAP 1 #define HAVE_MPROTECT 1 #define HAVE_NANOSLEEP 1 -#define HAVE_POSIX_MEMALIGN 1 #define HAVE_SCHED_GETAFFINITY 1 #define HAVE_SETCONSOLETEXTATTRIBUTE 0 #define HAVE_SETMODE 0 @@ -223,7 +219,7 @@ #define HAVE_STRERROR_R 1 #define HAVE_STRPTIME 1 #define HAVE_SYSCONF 1 -#define HAVE_SYSCTL 1 +#define HAVE_SYSCTL 0 #define HAVE_USLEEP 1 #define HAVE_VIRTUALALLOC 0 #define HAVE_PTHREADS 0 diff --git a/media/libav/config_win.asm b/media/libav/config_win.asm index 37a802f4c0..7ea033371b 100644 --- a/media/libav/config_win.asm +++ b/media/libav/config_win.asm @@ -138,7 +138,6 @@ %define HAVE_MACH_MACH_TIME_H 0 %define HAVE_MACHINE_IOCTL_BT848_H 0 %define HAVE_MACHINE_IOCTL_METEOR_H 0 -%define HAVE_MALLOC_H 1 %define HAVE_POLL_H 0 %define HAVE_SNDIO_H 0 %define HAVE_SOUNDCARD_H 0 @@ -200,15 +199,12 @@ %define HAVE_INET_ATON 0 %define HAVE_ISATTY 1 %define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -%define HAVE_LOCALTIME_R 0 %define HAVE_MACH_ABSOLUTE_TIME 0 %define HAVE_MAPVIEWOFFILE 1 -%define HAVE_MEMALIGN 0 %define HAVE_MKSTEMP 0 %define HAVE_MMAP 0 %define HAVE_MPROTECT 0 %define HAVE_NANOSLEEP 0 -%define HAVE_POSIX_MEMALIGN 0 %define HAVE_SCHED_GETAFFINITY 0 %define HAVE_SETCONSOLETEXTATTRIBUTE 1 %define HAVE_SETMODE 1 diff --git a/media/libav/config_win.h b/media/libav/config_win.h index b64f19c8d6..b40defe720 100644 --- a/media/libav/config_win.h +++ b/media/libav/config_win.h @@ -153,7 +153,6 @@ #define HAVE_MACH_MACH_TIME_H 0 #define HAVE_MACHINE_IOCTL_BT848_H 0 #define HAVE_MACHINE_IOCTL_METEOR_H 0 -#define HAVE_MALLOC_H 1 #define HAVE_POLL_H 0 #define HAVE_SNDIO_H 0 #define HAVE_SOUNDCARD_H 0 @@ -215,15 +214,12 @@ #define HAVE_INET_ATON 0 #define HAVE_ISATTY 1 #define HAVE_JACK_PORT_GET_LATENCY_RANGE 0 -#define HAVE_LOCALTIME_R 0 #define HAVE_MACH_ABSOLUTE_TIME 0 #define HAVE_MAPVIEWOFFILE 1 -#define HAVE_MEMALIGN 0 #define HAVE_MKSTEMP 0 #define HAVE_MMAP 0 #define HAVE_MPROTECT 0 #define HAVE_NANOSLEEP 0 -#define HAVE_POSIX_MEMALIGN 0 #define HAVE_SCHED_GETAFFINITY 0 #define HAVE_SETCONSOLETEXTATTRIBUTE 1 #define HAVE_SETMODE 1 diff --git a/media/libav/libavutil/internal.h b/media/libav/libavutil/internal.h index aed9925e7c..dd5e6a4eb1 100644 --- a/media/libav/libavutil/internal.h +++ b/media/libav/libavutil/internal.h @@ -134,7 +134,7 @@ #include "libm.h" -#if defined(_MSC_VER) +#if defined(_MSC_VER) && _MSC_VER < 1800 #pragma comment(linker, "/include:"EXTERN_PREFIX"avpriv_strtod") #pragma comment(linker, "/include:"EXTERN_PREFIX"avpriv_snprintf") #endif diff --git a/media/libav/moz.build b/media/libav/moz.build index 81cb29b422..e775ed52f5 100644 --- a/media/libav/moz.build +++ b/media/libav/moz.build @@ -17,16 +17,23 @@ EXPORTS.libavutil += [ 'libavutil/mem.h' ] +# These sources can't be unified because of macro name conflicts or needing to +# compile asm files separately. SOURCES += [ 'libavcodec/avfft.c', 'libavcodec/fft_fixed.c', + 'libavcodec/x86/fft.asm', + 'libavutil/dict.c', + 'libavutil/opt.c', + 'libavutil/x86/cpuid.asm', +] + +UNIFIED_SOURCES += [ 'libavcodec/fft_float.c', 'libavcodec/rdft.c', - 'libavcodec/x86/fft.asm', 'libavcodec/x86/fft_init.c', 'libavutil/avstring.c', 'libavutil/cpu.c', - 'libavutil/dict.c', 'libavutil/error.c', 'libavutil/eval.c', 'libavutil/file.c', @@ -36,17 +43,15 @@ SOURCES += [ 'libavutil/log2_tab.c', 'libavutil/mathematics.c', 'libavutil/mem.c', - 'libavutil/opt.c', 'libavutil/parseutils.c', 'libavutil/random_seed.c', 'libavutil/rational.c', 'libavutil/sha.c', - 'libavutil/x86/cpuid.asm' ] # Dummy functions are required for windows NoOpt/PGO builds. if CONFIG['_MSC_VER']: - SOURCES += [ + UNIFIED_SOURCES += [ 'avfft_dummy_funcs.c' ] diff --git a/media/libcubeb/AUTHORS b/media/libcubeb/AUTHORS index b441e8a1c3..394e9ae676 100644 --- a/media/libcubeb/AUTHORS +++ b/media/libcubeb/AUTHORS @@ -4,3 +4,5 @@ Michael Wu Paul Adenot David Richards Sebastien Alaiwan +KO Myung-Hun +Haakon Sporsheim diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index 6272a228c3..2578a03f51 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb git repository is: git://github.com/kinetiknz/cubeb.git -The git commit ID used was 588c82be50ffee59b7fab71b56e6081a5a89301c. +The git commit ID used was 77745f635240a5a85a2464bd3758c231045ac3d7. diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h index cd26dba786..4c016e0d23 100644 --- a/media/libcubeb/include/cubeb.h +++ b/media/libcubeb/include/cubeb.h @@ -152,6 +152,74 @@ enum { CUBEB_ERROR_NOT_SUPPORTED = -4 /**< Optional function not implemented in current backend. */ }; +typedef enum { + CUBEB_DEVICE_TYPE_UNKNOWN, + CUBEB_DEVICE_TYPE_INPUT, + CUBEB_DEVICE_TYPE_OUTPUT +} cubeb_device_type; + +typedef enum { + CUBEB_DEVICE_STATE_DISABLED, + CUBEB_DEVICE_STATE_UNPLUGGED, + CUBEB_DEVICE_STATE_ENABLED +} cubeb_device_state; + +typedef void * cubeb_devid; + +typedef enum { + CUBEB_DEVICE_FMT_S16LE = 0x0010, + CUBEB_DEVICE_FMT_S16BE = 0x0020, + CUBEB_DEVICE_FMT_F32LE = 0x1000, + CUBEB_DEVICE_FMT_F32BE = 0x2000 +} cubeb_device_fmt; + +#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) +#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE +#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE +#else +#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE +#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE +#endif +#define CUBEB_DEVICE_FMT_S16_MASK (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE) +#define CUBEB_DEVICE_FMT_F32_MASK (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE) +#define CUBEB_DEVICE_FMT_ALL (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK) + +typedef enum { + CUBEB_DEVICE_PREF_NONE = 0x00, + CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01, + CUBEB_DEVICE_PREF_VOICE = 0x02, + CUBEB_DEVICE_PREF_NOTIFICATION = 0x04, + CUBEB_DEVICE_PREF_ALL = 0x0F +} cubeb_device_pref; + +typedef struct { + cubeb_devid devid; /* Device identifier handle */ + char * device_id; /* Device identifier which might be presented in a UI */ + char * friendly_name; /* Friendly device name which might be presented in a UI */ + char * group_id; /* Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */ + char * vendor_name; /* Optional vendor name, may be NULL */ + + cubeb_device_type type; /* Type of device (Input/Output) */ + cubeb_device_state state; /* State of device disabled/enabled/unplugged */ + cubeb_device_pref preferred;/* Preferred device */ + + cubeb_device_fmt format; /* Sample format supported */ + cubeb_device_fmt default_format; + unsigned int max_channels; /* Channels */ + unsigned int default_rate; /* Default/Preferred sample rate */ + unsigned int max_rate; /* Maximum sample rate supported */ + unsigned int min_rate; /* Minimum sample rate supported */ + + unsigned int latency_lo_ms; /* Lowest possible latency in milliseconds */ + unsigned int latency_hi_ms; /* Higest possible latency in milliseconds */ +} cubeb_device_info; + +/** Device collection. */ +typedef struct { + uint32_t count; /**< Device count in collection. */ + cubeb_device_info * device[1]; /**< Array of pointers to device info. */ +} cubeb_device_collection; + /** User supplied data callback. @param stream @param user_ptr @@ -179,6 +247,12 @@ typedef void (* cubeb_state_callback)(cubeb_stream * stream, * @param user */ typedef void (* cubeb_device_changed_callback)(void * user_ptr); +/** + * User supplied callback called when the underlying device collection changed. + * @param context + * @param user_ptr */ +typedef void (* cubeb_device_collection_changed_callback)(cubeb * context, void * user_ptr); + /** Initialize an application context. This will perform any library or application scoped initialization. @param context @@ -337,6 +411,41 @@ int cubeb_stream_device_destroy(cubeb_stream * stream, int cubeb_stream_register_device_changed_callback(cubeb_stream * stream, cubeb_device_changed_callback device_changed_callback); +/** Returns enumerated devices. + @param context + @param devtype device type to include + @param collection output collection. Must be destroyed with cubeb_device_collection_destroy + @retval CUBEB_OK in case of success + @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer + @retval CUBEB_ERROR_NOT_SUPPORTED */ +int cubeb_enumerate_devices(cubeb * context, + cubeb_device_type devtype, + cubeb_device_collection ** collection); + +/** Destroy a cubeb_device_collection. + @param collection collection to destroy + @retval CUBEB_OK + @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */ +int cubeb_device_collection_destroy(cubeb_device_collection * collection); + +/** Destroy a cubeb_device_info structure. + @param info pointer to device info structure + @retval CUBEB_OK + @retval CUBEB_ERROR_INVALID_PARAMETER if info is an invalid pointer */ +int cubeb_device_info_destroy(cubeb_device_info * info); + +/** Registers a callback which is called when the system detects + a new device or a device is removed. + @param context + @param callback a function called whenever the system device list changes. + Passing NULL allow to unregister a function + @param user_ptr pointer to user specified data which will be present in + subsequent callbacks. + @retval CUBEB_ERROR_NOT_SUPPORTED */ +int cubeb_register_device_collection_changed(cubeb * context, + cubeb_device_collection_changed_callback callback, + void * user_ptr); + #if defined(__cplusplus) } #endif diff --git a/media/libcubeb/src/cubeb-internal.h b/media/libcubeb/src/cubeb-internal.h index 503b59a469..323510712b 100644 --- a/media/libcubeb/src/cubeb-internal.h +++ b/media/libcubeb/src/cubeb-internal.h @@ -8,6 +8,8 @@ #define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 #include "cubeb/cubeb.h" +#include +#include struct cubeb_ops { int (* init)(cubeb ** context, char const * context_name); @@ -17,6 +19,8 @@ struct cubeb_ops { cubeb_stream_params params, uint32_t * latency_ms); int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate); + int (* enumerate_devices)(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection); void (* destroy)(cubeb * context); int (* stream_init)(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, @@ -36,7 +40,6 @@ struct cubeb_ops { cubeb_device * device); int (* stream_register_device_changed_callback)(cubeb_stream * stream, cubeb_device_changed_callback device_changed_callback); - }; #define XASSERT(expr) do { \ diff --git a/media/libcubeb/src/cubeb.c b/media/libcubeb/src/cubeb.c index 8f3b040a0b..f22e8a9018 100644 --- a/media/libcubeb/src/cubeb.c +++ b/media/libcubeb/src/cubeb.c @@ -7,6 +7,7 @@ #undef NDEBUG #include #include +#include #if defined(HAVE_CONFIG_H) #include "config.h" #endif @@ -56,6 +57,9 @@ int opensl_init(cubeb ** context, char const * context_name); #if defined(USE_AUDIOTRACK) int audiotrack_init(cubeb ** context, char const * context_name); #endif +#if defined(USE_KAI) +int kai_init(cubeb ** context, char const * context_name); +#endif int validate_stream_params(cubeb_stream_params stream_params) @@ -89,12 +93,12 @@ int cubeb_init(cubeb ** context, char const * context_name) { int (* init[])(cubeb **, char const *) = { -#if defined(USE_PULSE) - pulse_init, -#endif #if defined(USE_JACK) jack_init, #endif +#if defined(USE_PULSE) + pulse_init, +#endif #if defined(USE_ALSA) alsa_init, #endif @@ -121,6 +125,9 @@ cubeb_init(cubeb ** context, char const * context_name) #endif #if defined(USE_AUDIOTRACK) audiotrack_init, +#endif +#if defined(USE_KAI) + kai_init, #endif }; int i; @@ -356,3 +363,50 @@ int cubeb_stream_register_device_changed_callback(cubeb_stream * stream, return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback); } + +int cubeb_enumerate_devices(cubeb * context, + cubeb_device_type devtype, + cubeb_device_collection ** collection) +{ + if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0) + return CUBEB_ERROR_INVALID_PARAMETER; + if (collection == NULL) + return CUBEB_ERROR_INVALID_PARAMETER; + if (!context->ops->enumerate_devices) + return CUBEB_ERROR_NOT_SUPPORTED; + + return context->ops->enumerate_devices(context, devtype, collection); +} + +int cubeb_device_collection_destroy(cubeb_device_collection * collection) +{ + uint32_t i; + + if (collection == NULL) + return CUBEB_ERROR_INVALID_PARAMETER; + + for (i = 0; i < collection->count; i++) + cubeb_device_info_destroy(collection->device[i]); + + free(collection); + return CUBEB_OK; +} + +int cubeb_device_info_destroy(cubeb_device_info * info) +{ + free(info->device_id); + free(info->friendly_name); + free(info->group_id); + free(info->vendor_name); + + free(info); + return CUBEB_OK; +} + +int cubeb_register_device_collection_changed(cubeb * context, + cubeb_device_collection_changed_callback callback, + void * user_ptr) +{ + return CUBEB_ERROR_NOT_SUPPORTED; +} + diff --git a/media/libcubeb/src/cubeb_alsa.c b/media/libcubeb/src/cubeb_alsa.c index 6ac7cb3afd..408fc6333d 100644 --- a/media/libcubeb/src/cubeb_alsa.c +++ b/media/libcubeb/src/cubeb_alsa.c @@ -968,7 +968,7 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { /* get a pcm, disabling resampling, so we get a rate the * hardware/dmix/pulse/etc. supports. */ - r = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0); + r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0); if (r < 0) { return CUBEB_ERROR; } @@ -1121,6 +1121,7 @@ static struct cubeb_ops const alsa_ops = { .get_max_channel_count = alsa_get_max_channel_count, .get_min_latency = alsa_get_min_latency, .get_preferred_sample_rate = alsa_get_preferred_sample_rate, + .enumerate_devices = NULL, .destroy = alsa_destroy, .stream_init = alsa_stream_init, .stream_destroy = alsa_stream_destroy, diff --git a/media/libcubeb/src/cubeb_audiotrack.c b/media/libcubeb/src/cubeb_audiotrack.c index e296e1d007..c1ce93c696 100644 --- a/media/libcubeb/src/cubeb_audiotrack.c +++ b/media/libcubeb/src/cubeb_audiotrack.c @@ -489,6 +489,7 @@ static struct cubeb_ops const audiotrack_ops = { .get_max_channel_count = audiotrack_get_max_channel_count, .get_min_latency = audiotrack_get_min_latency, .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate, + .enumerate_devices = NULL, .destroy = audiotrack_destroy, .stream_init = audiotrack_stream_init, .stream_destroy = audiotrack_stream_destroy, diff --git a/media/libcubeb/src/cubeb_audiounit.c b/media/libcubeb/src/cubeb_audiounit.c index 9b6b0e95a3..7506107792 100644 --- a/media/libcubeb/src/cubeb_audiounit.c +++ b/media/libcubeb/src/cubeb_audiounit.c @@ -159,7 +159,10 @@ audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags, pthread_mutex_unlock(&stm->mutex); if (stm->sample_spec.mChannelsPerFrame == 2) { - cubeb_pan_stereo_buffer_float((float*)buf, got, panning); + if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsFloat) + cubeb_pan_stereo_buffer_float((float*)buf, got, panning); + else if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsSignedInteger) + cubeb_pan_stereo_buffer_int((short*)buf, got, panning); } return noErr; @@ -400,6 +403,28 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range) } #endif /* !TARGET_OS_IPHONE */ +static AudioObjectID +audiounit_get_default_device_id(cubeb_device_type type) +{ + AudioObjectPropertyAddress adr = { 0, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + AudioDeviceID devid; + UInt32 size; + + if (type == CUBEB_DEVICE_TYPE_OUTPUT) + adr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + else if (type == CUBEB_DEVICE_TYPE_INPUT) + adr.mSelector = kAudioHardwarePropertyDefaultInputDevice; + else + return kAudioObjectUnknown; + + size = sizeof(AudioDeviceID); + if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, &devid) != noErr) { + return kAudioObjectUnknown; + } + + return devid; +} + int audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) { @@ -996,12 +1021,281 @@ int audiounit_stream_register_device_changed_callback(cubeb_stream * stream, return CUBEB_OK; } +static OSStatus +audiounit_get_devices(AudioObjectID ** devices, uint32_t * count) +{ + OSStatus ret; + UInt32 size = 0; + AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; + + ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size); + if (ret != noErr) + return ret; + + *count = (uint32_t)(size / sizeof(AudioObjectID)); + if (size >= sizeof(AudioObjectID)) { + if (*devices != NULL) free(*devices); + *devices = malloc(size); + memset(*devices, 0, size); + + ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, (void *)*devices); + if (ret != noErr) { + free(*devices); + *devices = NULL; + } + } else { + *devices = NULL; + } + + return ret; +} + +static char * +audiounit_strref_to_cstr_utf8(CFStringRef strref) { + CFIndex len, size; + char * ret; + if (strref == NULL) + return NULL; + + len = CFStringGetLength(strref); + size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8); + ret = malloc(size); + + if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) { + free(ret); + ret = NULL; + } + + return ret; +} + +static uint32_t +audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope) +{ + AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + UInt32 size = 0; + uint32_t i, ret = 0; + + adr.mSelector = kAudioDevicePropertyStreamConfiguration; + + if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) { + AudioBufferList * list = alloca(size); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) { + for (i = 0; i < list->mNumberBuffers; i++) + ret += list->mBuffers[i].mNumberChannels; + } + } + + return ret; +} + +static void +audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope, + uint32_t * min, uint32_t * max, uint32_t * def) +{ + AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + + adr.mSelector = kAudioDevicePropertyNominalSampleRate; + if (AudioObjectHasProperty(devid, &adr)) { + UInt32 size = sizeof(Float64); + Float64 fvalue = 0.0; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr) + *def = fvalue; + } + + adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + if (AudioObjectHasProperty(devid, &adr)) { + UInt32 size = 0; + AudioValueRange range; + if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) { + uint32_t i, count = size / sizeof(AudioValueRange); + AudioValueRange * ranges = malloc(size); + range.mMinimum = 9999999999.0; + range.mMaximum = 0.0; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges) == noErr) { + for (i = 0; i < count; i++) { + if (ranges[i].mMaximum > range.mMaximum) + range.mMaximum = ranges[i].mMaximum; + if (ranges[i].mMinimum < range.mMinimum) + range.mMinimum = ranges[i].mMinimum; + } + } + free(ranges); + } + *max = (uint32_t)range.mMaximum; + *min = (uint32_t)range.mMinimum; + } else { + *min = *max = 0; + } + +} + +static UInt32 +audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope) +{ + AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + UInt32 size, dev, stream = 0, offset; + AudioStreamID sid[1]; + + adr.mSelector = kAudioDevicePropertyLatency; + size = sizeof(UInt32); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) + dev = 0; + + adr.mSelector = kAudioDevicePropertyStreams; + size = sizeof(sid); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) { + adr.mSelector = kAudioStreamPropertyLatency; + size = sizeof(UInt32); + AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream); + } + + adr.mSelector = kAudioDevicePropertySafetyOffset; + size = sizeof(UInt32); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &offset) != noErr) + offset = 0; + + return dev + stream + offset; +} + +static cubeb_device_info * +audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type) +{ + AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster }; + UInt32 size, ch, latency; + cubeb_device_info * ret; + CFStringRef str = NULL; + AudioValueRange range; + + if (type == CUBEB_DEVICE_TYPE_OUTPUT) { + adr.mScope = kAudioDevicePropertyScopeOutput; + } else if (type == CUBEB_DEVICE_TYPE_INPUT) { + adr.mScope = kAudioDevicePropertyScopeInput; + } else { + return NULL; + } + + if ((ch = audiounit_get_channel_count(devid, adr.mScope)) == 0) + return NULL; + + ret = calloc(1, sizeof(cubeb_device_info)); + + size = sizeof(CFStringRef); + adr.mSelector = kAudioDevicePropertyDeviceUID; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { + ret->device_id = audiounit_strref_to_cstr_utf8(str); + ret->devid = (cubeb_devid)ret->device_id; + ret->group_id = strdup(ret->device_id); + CFRelease(str); + } + + size = sizeof(CFStringRef); + adr.mSelector = kAudioObjectPropertyName; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { + UInt32 ds; + size = sizeof(UInt32); + adr.mSelector = kAudioDevicePropertyDataSource; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds) == noErr) { + CFStringRef dsname; + AudioValueTranslation trl = { &ds, sizeof(ds), &dsname, sizeof(dsname) }; + adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString; + size = sizeof(AudioValueTranslation); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl) == noErr) { + CFStringRef fullstr = CFStringCreateWithFormat(NULL, NULL, + CFSTR("%@ (%@)"), str, dsname); + CFRelease(dsname); + if (fullstr != NULL) { + CFRelease(str); + str = fullstr; + } + } + } + + ret->friendly_name = audiounit_strref_to_cstr_utf8(str); + CFRelease(str); + } + + size = sizeof(CFStringRef); + adr.mSelector = kAudioObjectPropertyManufacturer; + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { + ret->vendor_name = audiounit_strref_to_cstr_utf8(str); + CFRelease(str); + } + + ret->type = type; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = (devid == audiounit_get_default_device_id(type)) ? + CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + + ret->max_channels = ch; + ret->format = CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */ + /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */ + ret->default_format = CUBEB_DEVICE_FMT_F32NE; + audiounit_get_available_samplerate(devid, adr.mScope, + &ret->min_rate, &ret->max_rate, &ret->default_rate); + + latency = audiounit_get_device_presentation_latency(devid, adr.mScope); + + adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + size = sizeof(AudioValueRange); + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range) == noErr) { + ret->latency_lo_ms = ((latency + range.mMinimum) * 1000) / ret->default_rate; + ret->latency_hi_ms = ((latency + range.mMaximum) * 1000) / ret->default_rate; + } else { + ret->latency_lo_ms = 10; /* Default to 10ms */ + ret->latency_hi_ms = 100; /* Default to 100ms */ + } + + return ret; +} + +static int +audiounit_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection) +{ + AudioObjectID * hwdevs = NULL; + uint32_t i, hwdevcount = 0; + OSStatus err; + + if ((err = audiounit_get_devices(&hwdevs, &hwdevcount)) != noErr) + return CUBEB_ERROR; + + *collection = malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0)); + (*collection)->count = 0; + + if (hwdevcount > 0) { + cubeb_device_info * cur; + + if (type & CUBEB_DEVICE_TYPE_OUTPUT) { + for (i = 0; i < hwdevcount; i++) { + if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_OUTPUT)) != NULL) + (*collection)->device[(*collection)->count++] = cur; + } + } + + if (type & CUBEB_DEVICE_TYPE_INPUT) { + for (i = 0; i < hwdevcount; i++) { + if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_INPUT)) != NULL) + (*collection)->device[(*collection)->count++] = cur; + } + } + } + + free(hwdevs); + + return CUBEB_OK; +} + static struct cubeb_ops const audiounit_ops = { .init = audiounit_init, .get_backend_id = audiounit_get_backend_id, .get_max_channel_count = audiounit_get_max_channel_count, .get_min_latency = audiounit_get_min_latency, .get_preferred_sample_rate = audiounit_get_preferred_sample_rate, + .enumerate_devices = audiounit_enumerate_devices, .destroy = audiounit_destroy, .stream_init = audiounit_stream_init, .stream_destroy = audiounit_stream_destroy, diff --git a/media/libcubeb/src/cubeb_opensl.c b/media/libcubeb/src/cubeb_opensl.c index 7f16614999..37d6af71d7 100644 --- a/media/libcubeb/src/cubeb_opensl.c +++ b/media/libcubeb/src/cubeb_opensl.c @@ -779,6 +779,7 @@ static struct cubeb_ops const opensl_ops = { .get_max_channel_count = opensl_get_max_channel_count, .get_min_latency = opensl_get_min_latency, .get_preferred_sample_rate = opensl_get_preferred_sample_rate, + .enumerate_devices = NULL, .destroy = opensl_destroy, .stream_init = opensl_stream_init, .stream_destroy = opensl_stream_destroy, diff --git a/media/libcubeb/src/cubeb_pulse.c b/media/libcubeb/src/cubeb_pulse.c index c1ac2fb21c..ef35661757 100644 --- a/media/libcubeb/src/cubeb_pulse.c +++ b/media/libcubeb/src/cubeb_pulse.c @@ -26,6 +26,8 @@ X(pa_context_drain) \ X(pa_context_get_server_info) \ X(pa_context_get_sink_info_by_name) \ + X(pa_context_get_sink_info_list) \ + X(pa_context_get_source_info_list) \ X(pa_context_get_state) \ X(pa_context_new) \ X(pa_context_rttime_new) \ @@ -37,6 +39,7 @@ X(pa_frame_size) \ X(pa_operation_get_state) \ X(pa_operation_unref) \ + X(pa_proplist_gets) \ X(pa_rtclock_now) \ X(pa_stream_begin_write) \ X(pa_stream_cancel_write) \ @@ -205,7 +208,7 @@ stream_request_callback(pa_stream * s, size_t nbytes, void * u) if (stm->volume != PULSE_NO_GAIN) { uint32_t samples = size * stm->sample_spec.channels / frame_size ; - if (stm->sample_spec.format == PA_SAMPLE_S16LE || + if (stm->sample_spec.format == PA_SAMPLE_S16BE || stm->sample_spec.format == PA_SAMPLE_S16LE) { short * b = buffer; for (uint32_t i = 0; i < samples; i++) { @@ -724,12 +727,221 @@ pulse_stream_set_panning(cubeb_stream * stream, float panning) return CUBEB_OK; } +typedef struct { + char * default_sink_name; + char * default_source_name; + + cubeb_device_info ** devinfo; + uint32_t max; + uint32_t count; +} pulse_dev_list_data; + +static cubeb_device_fmt +pulse_format_to_cubeb_format(pa_sample_format_t format) +{ + switch (format) { + case PA_SAMPLE_S16LE: + return CUBEB_DEVICE_FMT_S16LE; + case PA_SAMPLE_S16BE: + return CUBEB_DEVICE_FMT_S16BE; + case PA_SAMPLE_FLOAT32LE: + return CUBEB_DEVICE_FMT_F32LE; + case PA_SAMPLE_FLOAT32BE: + return CUBEB_DEVICE_FMT_F32BE; + default: + return CUBEB_DEVICE_FMT_F32NE; + } +} + +static void +pulse_ensure_dev_list_data_list_size (pulse_dev_list_data * list_data) +{ + if (list_data->count == list_data->max) { + list_data->max += 8; + list_data->devinfo = realloc(list_data->devinfo, + sizeof(cubeb_device_info) * list_data->max); + } +} + +static cubeb_device_state +pulse_get_state_from_sink_port(pa_sink_port_info * info) +{ + if (info != NULL) { +#if PA_CHECK_VERSION(2, 0, 0) + if (info->available == PA_PORT_AVAILABLE_NO) + return CUBEB_DEVICE_STATE_UNPLUGGED; + else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ +#endif + return CUBEB_DEVICE_STATE_ENABLED; + } + + return CUBEB_DEVICE_STATE_DISABLED; +} + +static void +pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, + int eol, void * user_data) +{ + pulse_dev_list_data * list_data = user_data; + cubeb_device_info * devinfo; + const char * prop; + + (void)context; + + if (eol || info == NULL) + return; + + devinfo = calloc(1, sizeof(cubeb_device_info)); + + devinfo->device_id = strdup(info->name); + devinfo->devid = (cubeb_devid)devinfo->device_id; + devinfo->friendly_name = strdup(info->description); + prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); + if (prop) + devinfo->group_id = strdup(prop); + prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name"); + if (prop) + devinfo->vendor_name = strdup(prop); + + devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT; + devinfo->state = pulse_get_state_from_sink_port(info->active_port); + devinfo->preferred = strcmp(info->name, list_data->default_sink_name) == 0; + + devinfo->format = CUBEB_DEVICE_FMT_ALL; + devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format); + devinfo->max_channels = info->channel_map.channels; + devinfo->min_rate = 1; + devinfo->max_rate = PA_RATE_MAX; + devinfo->default_rate = info->sample_spec.rate; + + devinfo->latency_lo_ms = 40; + devinfo->latency_hi_ms = 400; + + pulse_ensure_dev_list_data_list_size (list_data); + list_data->devinfo[list_data->count++] = devinfo; +} + +static cubeb_device_state +pulse_get_state_from_source_port(pa_source_port_info * info) +{ + if (info != NULL) { +#if PA_CHECK_VERSION(2, 0, 0) + if (info->available == PA_PORT_AVAILABLE_NO) + return CUBEB_DEVICE_STATE_UNPLUGGED; + else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ +#endif + return CUBEB_DEVICE_STATE_ENABLED; + } + + return CUBEB_DEVICE_STATE_DISABLED; +} + +static void +pulse_source_info_cb(pa_context * context, const pa_source_info * info, + int eol, void * user_data) +{ + pulse_dev_list_data * list_data = user_data; + cubeb_device_info * devinfo; + const char * prop; + + (void)context; + + if (eol) + return; + + devinfo = calloc(1, sizeof(cubeb_device_info)); + + devinfo->device_id = strdup(info->name); + devinfo->devid = (cubeb_devid)devinfo->device_id; + devinfo->friendly_name = strdup(info->description); + prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); + if (prop) + devinfo->group_id = strdup(prop); + prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name"); + if (prop) + devinfo->vendor_name = strdup(prop); + + devinfo->type = CUBEB_DEVICE_TYPE_INPUT; + devinfo->state = pulse_get_state_from_source_port(info->active_port); + devinfo->preferred = strcmp(info->name, list_data->default_source_name) == 0; + + devinfo->format = CUBEB_DEVICE_FMT_ALL; + devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format); + devinfo->max_channels = info->channel_map.channels; + devinfo->min_rate = 1; + devinfo->max_rate = PA_RATE_MAX; + devinfo->default_rate = info->sample_spec.rate; + + devinfo->latency_lo_ms = 1; + devinfo->latency_hi_ms = 10; + + pulse_ensure_dev_list_data_list_size (list_data); + list_data->devinfo[list_data->count++] = devinfo; +} + +static void +pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata) +{ + pulse_dev_list_data * list_data = userdata; + + (void)c; + + free(list_data->default_sink_name); + free(list_data->default_source_name); + list_data->default_sink_name = strdup(i->default_sink_name); + list_data->default_source_name = strdup(i->default_source_name); +} + +static int +pulse_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection) +{ + pulse_dev_list_data user_data = { NULL, NULL, NULL, 0, 0 }; + pa_operation * o; + uint32_t i; + + o = WRAP(pa_context_get_server_info)(context->context, + pulse_server_info_cb, &user_data); + if (o) { + operation_wait(context, NULL, o); + WRAP(pa_operation_unref)(o); + } + + if (type & CUBEB_DEVICE_TYPE_OUTPUT) { + o = WRAP(pa_context_get_sink_info_list)(context->context, + pulse_sink_info_cb, &user_data); + if (o) { + operation_wait(context, NULL, o); + WRAP(pa_operation_unref)(o); + } + } + + if (type & CUBEB_DEVICE_TYPE_INPUT) { + o = WRAP(pa_context_get_source_info_list)(context->context, + pulse_source_info_cb, &user_data); + if (o) { + operation_wait(context, NULL, o); + WRAP(pa_operation_unref)(o); + } + } + + *collection = malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info*) * (user_data.count > 0 ? user_data.count - 1 : 0)); + (*collection)->count = user_data.count; + for (i = 0; i < user_data.count; i++) + (*collection)->device[i] = user_data.devinfo[i]; + + free(user_data.devinfo); + return CUBEB_OK; +} + static struct cubeb_ops const pulse_ops = { .init = pulse_init, .get_backend_id = pulse_get_backend_id, .get_max_channel_count = pulse_get_max_channel_count, .get_min_latency = pulse_get_min_latency, .get_preferred_sample_rate = pulse_get_preferred_sample_rate, + .enumerate_devices = pulse_enumerate_devices, .destroy = pulse_destroy, .stream_init = pulse_stream_init, .stream_destroy = pulse_stream_destroy, diff --git a/media/libcubeb/src/cubeb_sndio.c b/media/libcubeb/src/cubeb_sndio.c index e6d531a4f1..94f9961735 100644 --- a/media/libcubeb/src/cubeb_sndio.c +++ b/media/libcubeb/src/cubeb_sndio.c @@ -356,6 +356,7 @@ static struct cubeb_ops const sndio_ops = { .get_max_channel_count = sndio_get_max_channel_count, .get_min_latency = sndio_get_min_latency, .get_preferred_sample_rate = sndio_get_preferred_sample_rate, + .enumerate_devices = NULL, .destroy = sndio_destroy, .stream_init = sndio_stream_init, .stream_destroy = sndio_stream_destroy, diff --git a/media/libcubeb/src/cubeb_wasapi.cpp b/media/libcubeb/src/cubeb_wasapi.cpp index 2ba8509bb7..c7b0e9530f 100644 --- a/media/libcubeb/src/cubeb_wasapi.cpp +++ b/media/libcubeb/src/cubeb_wasapi.cpp @@ -4,14 +4,15 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -// This enables assert in release, and lets us have debug-only code #if defined(HAVE_CONFIG_H) #include "config.h" #endif +#include #include #include #include #include +#include #include #include #include "cubeb/cubeb.h" @@ -22,11 +23,23 @@ #include #include -/**Taken from winbase.h, Not in MinGW.*/ +/* devicetopology.h missing in MinGW. */ +#ifndef __devicetopology_h__ +#include "cubeb_devicetopology.h" +#endif + +/* Taken from winbase.h, Not in MinGW. */ #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION #define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only #endif +#ifndef PKEY_Device_FriendlyName +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING +#endif +#ifndef PKEY_Device_InstanceId +DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x00000100); // VT_LPWSTR +#endif + // #define LOGGING_ENABLED #ifdef LOGGING_ENABLED @@ -48,13 +61,13 @@ ms_to_hns(uint32_t ms) } uint32_t -hns_to_ms(uint32_t hns) +hns_to_ms(REFERENCE_TIME hns) { return hns / 10000; } double -hns_to_s(uint32_t hns) +hns_to_s(REFERENCE_TIME hns) { return static_cast(hns) / 10000000; } @@ -76,7 +89,7 @@ void SafeRelease(T * ptr) } /* This wraps a critical section to track the owner in debug mode, adapted from - * NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */ + NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */ class owned_critical_section { public: @@ -112,7 +125,7 @@ public: } /* This is guaranteed to have the good behaviour if it succeeds. The behaviour - * is undefined otherwise. */ + is undefined otherwise. */ void assert_current_thread_owns() { #ifdef DEBUG @@ -150,12 +163,12 @@ struct auto_com { if (result == RPC_E_CHANGED_MODE) { // This is not an error, COM was not initialized by this function, so it is // not necessary to uninit it. - LOG("COM already initialized in STA.\n"); + LOG("COM was already initialized in STA.\n"); } else if (result == S_FALSE) { // This is not an error. We are allowed to call CoInitializeEx more than // once, as long as it is matches by an CoUninitialize call. // We do that in the dtor which is guaranteed to be called. - LOG("COM already initialized in MTA\n"); + LOG("COM was already initialized in MTA\n"); } if (SUCCEEDED(result)) { CoUninitialize(); @@ -184,8 +197,8 @@ int setup_wasapi_stream(cubeb_stream * stm); struct cubeb { cubeb_ops const * ops; - /* Library dynamically opened to increase the render - * thread priority, and the two function pointers we need. */ + /* Library dynamically opened to increase the render thread priority, and + the two function pointers we need. */ HMODULE mmcss_module; set_mm_thread_characteristics_function set_mm_thread_characteristics; revert_mm_thread_characteristics_function revert_mm_thread_characteristics; @@ -196,9 +209,9 @@ class wasapi_endpoint_notification_client; struct cubeb_stream { cubeb * context; - /* Mixer pameters. We need to convert the input - * stream to this samplerate/channel layout, as WASAPI - * does not resample nor upmix itself. */ + /* Mixer pameters. We need to convert the input stream to this + samplerate/channel layout, as WASAPI * does not resample nor upmix + itself. */ cubeb_stream_params mix_params; cubeb_stream_params stream_params; /* The latency initially requested for this stream. */ @@ -208,10 +221,10 @@ struct cubeb_stream void * user_ptr; /* Lifetime considerations: - * - client, render_client, audio_clock and audio_stream_volume are interface - * pointer to the IAudioClient. - * - The lifetime for device_enumerator and notification_client, resampler, - * mix_buffer are the same as the cubeb_stream instance. */ + - client, render_client, audio_clock and audio_stream_volume are interface + pointer to the IAudioClient. + - The lifetime for device_enumerator and notification_client, resampler, + mix_buffer are the same as the cubeb_stream instance. */ /* Main handle on the WASAPI stream. */ IAudioClient * client; @@ -222,23 +235,23 @@ struct cubeb_stream /* Interface pointer to use the stream audio clock. */ IAudioClock * audio_clock; /* Frames written to the stream since it was opened. Reset on device - * change. Uses mix_params.rate. */ + change. Uses mix_params.rate. */ UINT64 frames_written; /* Frames written to the (logical) stream since it was first - * created. Updated on device change. Uses stream_params.rate. */ + created. Updated on device change. Uses stream_params.rate. */ UINT64 total_frames_written; /* Last valid reported stream position. Used to ensure the position - * reported by stream_get_position increases monotonically. */ + reported by stream_get_position increases monotonically. */ UINT64 prev_position; /* Device enumerator to be able to be notified when the default - * device change. */ + device change. */ IMMDeviceEnumerator * device_enumerator; /* Device notification client, to be able to be notified when the default - * audio device changes and route the audio to the new default audio output - * device */ + audio device changes and route the audio to the new default audio output + device */ wasapi_endpoint_notification_client * notification_client; /* This event is set by the stream_stop and stream_destroy - * function, so the render loop can exit properly. */ + function, so the render loop can exit properly. */ HANDLE shutdown_event; /* Set by OnDefaultDeviceChanged when a stream reconfiguration is required. The reconfiguration is handled by the render loop thread. */ @@ -256,10 +269,10 @@ struct cubeb_stream /* Resampler instance. Resampling will only happen if necessary. */ cubeb_resampler * resampler; /* Buffer used to downmix or upmix to the number of channels the mixer has. - * its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */ + its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */ float * mix_buffer; /* Stream volume. Set via stream_set_volume and used to reset volume on - * device changes. */ + device changes. */ float volume; /* True if the stream is draining. */ bool draining; @@ -307,9 +320,14 @@ public: , reconfigure_event(event) { } + virtual ~wasapi_endpoint_notification_client() + { } + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id) { + LOG("Audio device default changed.\n"); + /* we only support a single stream type for now. */ if (flow != eRender && role != eMultimedia) { return S_OK; @@ -317,14 +335,14 @@ public: BOOL ok = SetEvent(reconfigure_event); if (!ok) { - LOG("SetEvent on reconfigure_event failed: %x", GetLastError()); + LOG("SetEvent on reconfigure_event failed: %x\n", GetLastError()); } return S_OK; } /* The remaining methods are not implemented, they simply log when called (if - * log is enabled), for debugging. */ + log is enabled), for debugging. */ HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id) { LOG("Audio device added.\n"); @@ -422,8 +440,8 @@ downmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channel { XASSERT(in_channels >= out_channels); /* We could use a downmix matrix here, applying mixing weight based on the - * channel, but directsound and winmm simply drop the channels that cannot be - * rendered by the hardware, so we do the same for consistency. */ + channel, but directsound and winmm simply drop the channels that cannot be + rendered by the hardware, so we do the same for consistency. */ long out_index = 0; for (long i = 0; i < inframes * in_channels; i += in_channels) { for (int j = 0; j < out_channels; ++j) { @@ -433,8 +451,8 @@ downmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channel } } -/* This returns the size of a frame in the stream, - * before the eventual upmix occurs. */ +/* This returns the size of a frame in the stream, before the eventual upmix + occurs. */ static size_t frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames) { @@ -446,7 +464,7 @@ long refill(cubeb_stream * stm, float * data, long frames_needed) { /* If we need to upmix after resampling, resample into the mix buffer to - * avoid a copy. */ + avoid a copy. */ float * dest; if (should_upmix(stm) || should_downmix(stm)) { dest = stm->mix_buffer; @@ -465,12 +483,12 @@ refill(cubeb_stream * stm, float * data, long frames_needed) /* Go in draining mode if we got fewer frames than requested. */ if (out_frames < frames_needed) { - LOG("draining.\n"); + LOG("start draining.\n"); stm->draining = true; } /* If this is not true, there will be glitches. - * It is alright to have produced less frames if we are draining, though. */ + It is alright to have produced less frames if we are draining, though. */ XASSERT(out_frames == frames_needed || stm->draining); if (should_upmix(stm)) { @@ -493,16 +511,16 @@ wasapi_stream_render_loop(LPVOID stream) HANDLE wait_array[3] = {stm->shutdown_event, stm->reconfigure_event, stm->refill_event}; HANDLE mmcss_handle = NULL; HRESULT hr = 0; - bool first = true; DWORD mmcss_task_index = 0; auto_com com; if (!com.ok()) { LOG("COM initialization failed on render_loop thread.\n"); + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); return 0; } /* We could consider using "Pro Audio" here for WebAudio and - * maybe WebRTC. */ + maybe WebRTC. */ mmcss_handle = stm->context->set_mm_thread_characteristics("Audio", &mmcss_task_index); if (!mmcss_handle) { @@ -529,7 +547,7 @@ wasapi_stream_render_loop(LPVOID stream) case WAIT_OBJECT_0: { /* shutdown */ is_playing = false; /* We don't check if the drain is actually finished here, we just want to - * shutdown. */ + shutdown. */ if (stm->draining) { stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); } @@ -542,13 +560,13 @@ wasapi_stream_render_loop(LPVOID stream) auto_lock lock(stm->stream_reset_lock); close_wasapi_stream(stm); /* Reopen a stream and start it immediately. This will automatically pick the - * new default device for this role. */ + new default device for this role. */ int r = setup_wasapi_stream(stm); if (r != CUBEB_OK) { /* Don't destroy the stream here, since we expect the caller to do so after the error has propagated via the state callback. */ is_playing = false; - hr = -1; + hr = E_FAIL; continue; } } @@ -560,14 +578,12 @@ wasapi_stream_render_loop(LPVOID stream) hr = stm->client->GetCurrentPadding(&padding); if (FAILED(hr)) { - LOG("Failed to get padding\n"); + LOG("Failed to get padding: %x\n", hr); is_playing = false; continue; } XASSERT(padding <= stm->buffer_frame_count); - long available = stm->buffer_frame_count - padding; - if (stm->draining) { if (padding == 0) { stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); @@ -576,6 +592,8 @@ wasapi_stream_render_loop(LPVOID stream) continue; } + long available = stm->buffer_frame_count - padding; + if (available == 0) { continue; } @@ -588,11 +606,11 @@ wasapi_stream_render_loop(LPVOID stream) hr = stm->render_client->ReleaseBuffer(wrote, 0); if (FAILED(hr)) { - LOG("failed to release buffer.\n"); + LOG("failed to release buffer: %x\n", hr); is_playing = false; } } else { - LOG("failed to get buffer.\n"); + LOG("failed to get buffer: %x\n", hr); is_playing = false; } } @@ -601,7 +619,7 @@ wasapi_stream_render_loop(LPVOID stream) XASSERT(stm->shutdown_event == wait_array[0]); if (++timeout_count >= timeout_limit) { is_playing = false; - hr = -1; + hr = E_FAIL; } break; default: @@ -636,7 +654,6 @@ HRESULT register_notification_client(cubeb_stream * stm) HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&stm->device_enumerator)); - if (FAILED(hr)) { LOG("Could not get device enumerator: %x\n", hr); return hr; @@ -645,7 +662,6 @@ HRESULT register_notification_client(cubeb_stream * stm) stm->notification_client = new wasapi_endpoint_notification_client(stm->reconfigure_event); hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client); - if (FAILED(hr)) { LOG("Could not register endpoint notification callback: %x\n", hr); return hr; @@ -677,16 +693,16 @@ HRESULT get_default_endpoint(IMMDevice ** device) NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator)); if (FAILED(hr)) { - LOG("Could not get device enumerator.\n"); + LOG("Could not get device enumerator: %x\n", hr); return hr; } /* eMultimedia is okay for now ("Music, movies, narration, [...]"). - * We will need to change this when we distinguish streams by use-case, other - * possible values being eConsole ("Games, system notification sounds [...]") - * and eCommunication ("Voice communication"). */ + We will need to change this when we distinguish streams by use-case, other + possible values being eConsole ("Games, system notification sounds [...]") + and eCommunication ("Voice communication"). */ hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, device); if (FAILED(hr)) { - LOG("Could not get default audio endpoint. %d\n", __LINE__); + LOG("Could not get default audio endpoint: %x\n", hr); SafeRelease(enumerator); return hr; } @@ -781,12 +797,15 @@ int wasapi_init(cubeb ** context, char const * context_name) IMMDevice * device; hr = get_default_endpoint(&device); if (FAILED(hr)) { - LOG("Could not get device.\n"); + LOG("Could not get device: %x\n", hr); return CUBEB_ERROR; } SafeRelease(device); cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb)); + if (!ctx) { + return CUBEB_ERROR; + } ctx->ops = &wasapi_ops; @@ -909,7 +928,7 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten IMMDevice * device; hr = get_default_endpoint(&device); if (FAILED(hr)) { - LOG("Could not get default endpoint:%x.\n", hr); + LOG("Could not get default endpoint: %x\n", hr); return CUBEB_ERROR; } @@ -918,7 +937,7 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten NULL, (void **)&client); SafeRelease(device); if (FAILED(hr)) { - LOG("Could not activate device for latency: %x.\n", hr); + LOG("Could not activate device for latency: %x\n", hr); return CUBEB_ERROR; } @@ -926,15 +945,15 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten hr = client->GetDevicePeriod(&default_period, NULL); if (FAILED(hr)) { SafeRelease(client); - LOG("Could not get device period: %x.\n", hr); + LOG("Could not get device period: %x\n", hr); return CUBEB_ERROR; } LOG("default device period: %ld\n", default_period); /* According to the docs, the best latency we can achieve is by synchronizing - * the stream and the engine. - * http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */ + the stream and the engine. + http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */ *latency_ms = hns_to_ms(default_period); SafeRelease(client); @@ -983,22 +1002,22 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) void wasapi_stream_destroy(cubeb_stream * stm); -/* Based on the mix format and the stream format, try to find a way to play what - * the user requested. */ +/* Based on the mix format and the stream format, try to find a way to play + what the user requested. */ static void handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cubeb_stream_params * stream_params) { /* Common case: the hardware is stereo. Up-mixing and down-mixing will be - * handled in the callback. */ + handled in the callback. */ if ((*mix_format)->nChannels <= 2) { return; } /* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1], - * so the reinterpret_cast below should be safe. In practice, this is not - * true, and we just want to bail out and let the rest of the code find a good - * conversion path instead of trying to make WASAPI do it by itself. - * [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/ + so the reinterpret_cast below should be safe. In practice, this is not + true, and we just want to bail out and let the rest of the code find a good + conversion path instead of trying to make WASAPI do it by itself. + [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/ if ((*mix_format)->wFormatTag != WAVE_FORMAT_EXTENSIBLE) { return; } @@ -1009,7 +1028,7 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm; /* The hardware is in surround mode, we want to only use front left and front - * right. Try that, and check if it works. */ + right. Try that, and check if it works. */ switch (stream_params->channels) { case 1: /* Mono */ format_pcm->dwChannelMask = KSAUDIO_SPEAKER_MONO; @@ -1036,7 +1055,7 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub if (hr == S_FALSE) { /* Not supported, but WASAPI gives us a suggestion. Use it, and handle the - * eventual upmix/downmix ourselves */ + eventual upmix/downmix ourselves */ LOG("Using WASAPI suggested format: channels: %d\n", closest->nChannels); WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast(closest); XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat); @@ -1044,11 +1063,13 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub *mix_format = closest; } else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) { /* Not supported, no suggestion. This should not happen, but it does in the - * field with some sound cards. We restore the mix format, and let the rest - * of the code figure out the right conversion path. */ + field with some sound cards. We restore the mix format, and let the rest + of the code figure out the right conversion path. */ *reinterpret_cast(*mix_format) = hw_mix_format; } else if (hr == S_OK) { LOG("Requested format accepted by WASAPI.\n"); + } else { + LOG("IsFormatSupported unhandled error: %x\n", hr); } } @@ -1074,7 +1095,7 @@ int setup_wasapi_stream(cubeb_stream * stm) } /* Get a client. We will get all other interfaces we need from - * this pointer. */ + this pointer. */ hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void **)&stm->client); @@ -1085,7 +1106,7 @@ int setup_wasapi_stream(cubeb_stream * stm) } /* We have to distinguish between the format the mixer uses, - * and the format the stream we want to play uses. */ + and the format the stream we want to play uses. */ hr = stm->client->GetMixFormat(&mix_format); if (FAILED(hr)) { LOG("Could not fetch current mix format from the audio client: error: %x\n", hr); @@ -1095,7 +1116,7 @@ int setup_wasapi_stream(cubeb_stream * stm) handle_channel_layout(stm, &mix_format, &stm->stream_params); /* Shared mode WASAPI always supports float32 sample format, so this - * is safe. */ + is safe. */ stm->mix_params.format = CUBEB_SAMPLE_FLOAT32NE; stm->mix_params.rate = mix_format->nSamplesPerSec; stm->mix_params.channels = mix_format->nChannels; @@ -1107,17 +1128,15 @@ int setup_wasapi_stream(cubeb_stream * stm) 0, mix_format, NULL); - CoTaskMemFree(mix_format); - if (FAILED(hr)) { - LOG("Unable to initialize audio client: %x.\n", hr); + LOG("Unable to initialize audio client: %x\n", hr); return CUBEB_ERROR; } hr = stm->client->GetBufferSize(&stm->buffer_frame_count); if (FAILED(hr)) { - LOG("Could not get the buffer size from the client %x.\n", hr); + LOG("Could not get the buffer size from the client: %x\n", hr); return CUBEB_ERROR; } @@ -1127,21 +1146,21 @@ int setup_wasapi_stream(cubeb_stream * stm) hr = stm->client->SetEventHandle(stm->refill_event); if (FAILED(hr)) { - LOG("Could set the event handle for the client %x.\n", hr); + LOG("Could set the event handle for the client: %x\n", hr); return CUBEB_ERROR; } hr = stm->client->GetService(__uuidof(IAudioRenderClient), (void **)&stm->render_client); if (FAILED(hr)) { - LOG("Could not get the render client %x.\n", hr); + LOG("Could not get the render client: %x\n", hr); return CUBEB_ERROR; } hr = stm->client->GetService(__uuidof(IAudioStreamVolume), (void **)&stm->audio_stream_volume); if (FAILED(hr)) { - LOG("Could not get the IAudioStreamVolume %x.\n", hr); + LOG("Could not get the IAudioStreamVolume: %x\n", hr); return CUBEB_ERROR; } @@ -1149,7 +1168,7 @@ int setup_wasapi_stream(cubeb_stream * stm) hr = stm->client->GetService(__uuidof(IAudioClock), (void **)&stm->audio_clock); if (FAILED(hr)) { - LOG("Could not get the IAudioClock %x.\n", hr); + LOG("Could not get the IAudioClock: %x\n", hr); return CUBEB_ERROR; } @@ -1159,9 +1178,9 @@ int setup_wasapi_stream(cubeb_stream * stm) } /* If we are playing a mono stream, we only resample one channel, - * and copy it over, so we are always resampling the number - * of channels of the stream, not the number of channels - * that WASAPI wants. */ + and copy it over, so we are always resampling the number + of channels of the stream, not the number of channels + that WASAPI wants. */ stm->resampler = cubeb_resampler_create(stm, stm->stream_params, stm->mix_params.rate, stm->data_callback, @@ -1191,6 +1210,10 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, XASSERT(context && stream); + if (stream_params.format != CUBEB_SAMPLE_FLOAT32NE) { + return CUBEB_ERROR_INVALID_FORMAT; + } + cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream)); XASSERT(stm); @@ -1235,7 +1258,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, hr = register_notification_client(stm); if (FAILED(hr)) { /* this is not fatal, we can still play audio, but we won't be able - * to keep using the default audio endpoint if it changes. */ + to keep using the default audio endpoint if it changes. */ LOG("failed to register notification client, %x\n", hr); } @@ -1325,7 +1348,7 @@ int wasapi_stream_start(cubeb_stream * stm) LOG("could not start the stream after reconfig: %x\n", hr); return CUBEB_ERROR; } - } else if (FAILED(hr)) { + } else if (FAILED(hr)) { LOG("could not start the stream.\n"); return CUBEB_ERROR; } @@ -1402,13 +1425,16 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) auto_lock lock(stm->stream_reset_lock); /* The GetStreamLatency method only works if the - * AudioClient has been initialized. */ + AudioClient has been initialized. */ if (!stm->client) { return CUBEB_ERROR; } REFERENCE_TIME latency_hns; - stm->client->GetStreamLatency(&latency_hns); + HRESULT hr = stm->client->GetStreamLatency(&latency_hns); + if (FAILED(hr)) { + return CUBEB_ERROR; + } double latency_s = hns_to_s(latency_hns); *latency = static_cast(latency_s * stm->stream_params.rate); @@ -1428,12 +1454,245 @@ int wasapi_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } +static char * +wstr_to_utf8(LPCWSTR str) +{ + char * ret = NULL; + int size; + + size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, 0, NULL, NULL); + if (size > 0) { + ret = (char *) malloc(size); + ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL); + } + + return ret; +} + +static IMMDevice * +wasapi_get_device_node(IMMDeviceEnumerator * enumerator, IMMDevice * dev) +{ + IMMDevice * ret = NULL; + IDeviceTopology * devtopo = NULL; + IConnector * connector = NULL; + + if (SUCCEEDED(dev->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&devtopo)) && + SUCCEEDED(devtopo->GetConnector(0, &connector))) { + LPWSTR filterid; + if (SUCCEEDED(connector->GetDeviceIdConnectedTo(&filterid))) { + if (FAILED(enumerator->GetDevice(filterid, &ret))) + ret = NULL; + CoTaskMemFree(filterid); + } + } + + SafeRelease(connector); + SafeRelease(devtopo); + return ret; +} + +static BOOL +wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id, + IMMDeviceEnumerator * enumerator) +{ + BOOL ret = FALSE; + IMMDevice * dev; + HRESULT hr; + + hr = enumerator->GetDefaultAudioEndpoint(flow, role, &dev); + if (SUCCEEDED(hr)) { + LPWSTR defdevid = NULL; + if (SUCCEEDED(dev->GetId(&defdevid))) + ret = (wcscmp(defdevid, device_id) == 0); + if (defdevid != NULL) + CoTaskMemFree(defdevid); + SafeRelease(dev); + } + + return ret; +} + +static cubeb_device_info * +wasapi_create_device(IMMDeviceEnumerator * enumerator, IMMDevice * dev) +{ + IMMEndpoint * endpoint = NULL; + IMMDevice * devnode; + IAudioClient * client = NULL; + cubeb_device_info * ret = NULL; + EDataFlow flow; + LPWSTR device_id = NULL; + DWORD state = DEVICE_STATE_NOTPRESENT; + IPropertyStore * propstore = NULL; + PROPVARIANT propvar; + REFERENCE_TIME def_period, min_period; + HRESULT hr; + + PropVariantInit(&propvar); + + hr = dev->QueryInterface(IID_PPV_ARGS(&endpoint)); + if (FAILED(hr)) goto done; + + hr = endpoint->GetDataFlow(&flow); + if (FAILED(hr)) goto done; + + hr = dev->GetId(&device_id); + if (FAILED(hr)) goto done; + + hr = dev->OpenPropertyStore(STGM_READ, &propstore); + if (FAILED(hr)) goto done; + + hr = dev->GetState(&state); + if (FAILED(hr)) goto done; + + ret = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info)); + + ret->devid = ret->device_id = wstr_to_utf8(device_id); + hr = propstore->GetValue(PKEY_Device_FriendlyName, &propvar); + if (SUCCEEDED(hr)) + ret->friendly_name = wstr_to_utf8(propvar.pwszVal); + + devnode = wasapi_get_device_node(enumerator, dev); + if (devnode != NULL) { + IPropertyStore * ps = NULL; + hr = devnode->OpenPropertyStore(STGM_READ, &ps); + if (FAILED(hr)) goto done; + + PropVariantClear(&propvar); + hr = ps->GetValue(PKEY_Device_InstanceId, &propvar); + if (SUCCEEDED(hr)) { + ret->group_id = wstr_to_utf8(propvar.pwszVal); + } + SafeRelease(ps); + } + + ret->preferred = CUBEB_DEVICE_PREF_NONE; + if (wasapi_is_default_device(flow, eMultimedia, device_id, enumerator)) + ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_MULTIMEDIA); + if (wasapi_is_default_device(flow, eCommunications, device_id, enumerator)) + ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_VOICE); + if (wasapi_is_default_device(flow, eConsole, device_id, enumerator)) + ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_NOTIFICATION); + + if (flow == eRender) ret->type = CUBEB_DEVICE_TYPE_OUTPUT; + else if (flow == eCapture) ret->type = CUBEB_DEVICE_TYPE_INPUT; + switch (state) { + case DEVICE_STATE_ACTIVE: + ret->state = CUBEB_DEVICE_STATE_ENABLED; + break; + case DEVICE_STATE_UNPLUGGED: + ret->state = CUBEB_DEVICE_STATE_UNPLUGGED; + break; + default: + ret->state = CUBEB_DEVICE_STATE_DISABLED; + break; + }; + + ret->format = CUBEB_DEVICE_FMT_F32NE; /* cubeb only supports 32bit float at the moment */ + ret->default_format = CUBEB_DEVICE_FMT_F32NE; + PropVariantClear(&propvar); + hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &propvar); + if (SUCCEEDED(hr) && propvar.vt == VT_BLOB) { + if (propvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) { + const PCMWAVEFORMAT * pcm = reinterpret_cast(propvar.blob.pBlobData); + + ret->max_rate = ret->min_rate = ret->default_rate = pcm->wf.nSamplesPerSec; + ret->max_channels = pcm->wf.nChannels; + } else if (propvar.blob.cbSize >= sizeof(WAVEFORMATEX)) { + WAVEFORMATEX* wfx = reinterpret_cast(propvar.blob.pBlobData); + + if (propvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize || + wfx->wFormatTag == WAVE_FORMAT_PCM) { + ret->max_rate = ret->min_rate = ret->default_rate = wfx->nSamplesPerSec; + ret->max_channels = wfx->nChannels; + } + } + } + + if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&client)) && + SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) { + ret->latency_lo_ms = hns_to_ms(min_period); + ret->latency_hi_ms = hns_to_ms(def_period); + } else { + ret->latency_lo_ms = 0; + ret->latency_hi_ms = 0; + } + SafeRelease(client); + +done: + SafeRelease(devnode); + SafeRelease(endpoint); + SafeRelease(propstore); + if (device_id != NULL) + CoTaskMemFree(device_id); + PropVariantClear(&propvar); + return ret; +} + +static int +wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** out) +{ + auto_com com; + IMMDeviceEnumerator * enumerator; + IMMDeviceCollection * collection; + IMMDevice * dev; + cubeb_device_info * cur; + HRESULT hr; + UINT cc, i; + EDataFlow flow; + + *out = NULL; + + if (!com.ok()) + return CUBEB_ERROR; + + hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator)); + if (FAILED(hr)) { + LOG("Could not get device enumerator: %x\n", hr); + return CUBEB_ERROR; + } + + if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender; + else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture; + else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_INPUT)) flow = eAll; + else return CUBEB_ERROR; + + hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, &collection); + if (FAILED(hr)) { + LOG("Could not enumerate audio endpoints: %x\n", hr); + return CUBEB_ERROR; + } + + hr = collection->GetCount(&cc); + if (FAILED(hr)) { + LOG("IMMDeviceCollection::GetCount() failed: %x\n", hr); + return CUBEB_ERROR; + } + *out = (cubeb_device_collection *) malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info*) * (cc > 0 ? cc - 1 : 0)); + (*out)->count = 0; + for (i = 0; i < cc; i++) { + hr = collection->Item(i, &dev); + if (FAILED(hr)) { + LOG("IMMDeviceCollection::Item(%u) failed: %x\n", i-1, hr); + } else if ((cur = wasapi_create_device(enumerator, dev)) != NULL) { + (*out)->device[(*out)->count++] = cur; + } + } + + SafeRelease(collection); + SafeRelease(enumerator); + return CUBEB_OK; +} + cubeb_ops const wasapi_ops = { /*.init =*/ wasapi_init, /*.get_backend_id =*/ wasapi_get_backend_id, /*.get_max_channel_count =*/ wasapi_get_max_channel_count, /*.get_min_latency =*/ wasapi_get_min_latency, /*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate, + /*.enumerate_devices =*/ wasapi_enumerate_devices, /*.destroy =*/ wasapi_destroy, /*.stream_init =*/ wasapi_stream_init, /*.stream_destroy =*/ wasapi_stream_destroy, diff --git a/media/libcubeb/src/cubeb_winmm.c b/media/libcubeb/src/cubeb_winmm.c index 0b29f222b3..7f1c7ee11d 100644 --- a/media/libcubeb/src/cubeb_winmm.c +++ b/media/libcubeb/src/cubeb_winmm.c @@ -6,9 +6,8 @@ */ #define __MSVCRT_VERSION__ 0x0700 #undef WINVER -#define WINVER 0x0600 +#define WINVER 0x0501 #undef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN #include #include @@ -27,15 +26,46 @@ #endif /**This is also missing from the MinGW headers. It also appears to be undocumented by Microsoft.*/ +#ifndef WAVE_FORMAT_48M08 +#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */ +#endif +#ifndef WAVE_FORMAT_48M16 +#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */ +#endif +#ifndef WAVE_FORMAT_48S08 +#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */ +#endif #ifndef WAVE_FORMAT_48S16 #define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */ #endif +#ifndef WAVE_FORMAT_96M08 +#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ +#endif +#ifndef WAVE_FORMAT_96M16 +#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */ +#endif +#ifndef WAVE_FORMAT_96S08 +#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */ +#endif +#ifndef WAVE_FORMAT_96S16 +#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ +#endif /**Taken from winbase.h, also not in MinGW.*/ #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION #define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only #endif +#ifndef DRVM_MAPPER +#define DRVM_MAPPER (0x2000) +#endif +#ifndef DRVM_MAPPER_PREFERRED_GET +#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21) +#endif +#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET +#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23) +#endif + #define CUBEB_STREAM_MAX 32 #define NBUFS 4 @@ -736,12 +766,307 @@ winmm_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } +#define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16) +#define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16) +#define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16) +#define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16) +#define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16) +static void +winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats) +{ + if (formats & MM_11025HZ_MASK) { + info->min_rate = 11025; + info->default_rate = 11025; + info->max_rate = 11025; + } + if (formats & MM_22050HZ_MASK) { + if (info->min_rate == 0) info->min_rate = 22050; + info->max_rate = 22050; + info->default_rate = 22050; + } + if (formats & MM_44100HZ_MASK) { + if (info->min_rate == 0) info->min_rate = 44100; + info->max_rate = 44100; + info->default_rate = 44100; + } + if (formats & MM_48000HZ_MASK) { + if (info->min_rate == 0) info->min_rate = 48000; + info->max_rate = 48000; + info->default_rate = 48000; + } + if (formats & MM_96000HZ_MASK) { + if (info->min_rate == 0) { + info->min_rate = 96000; + info->default_rate = 96000; + } + info->max_rate = 96000; + } +} + + +#define MM_S16_MASK (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | \ + WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16) +static int +winmm_query_supported_formats(UINT devid, DWORD formats, + cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt) +{ + WAVEFORMATEXTENSIBLE wfx; + + if (formats & MM_S16_MASK) + *deffmt = *supfmt = CUBEB_DEVICE_FMT_S16LE; + else + *deffmt = *supfmt = 0; + + ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE)); + wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wfx.Format.nChannels = 2; + wfx.Format.nSamplesPerSec = 44100; + wfx.Format.wBitsPerSample = 32; + wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8; + wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; + wfx.Format.cbSize = 22; + wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample; + wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR) + *supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE); + + return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR; +} + +static char * +guid_to_cstr(LPGUID guid) +{ + char * ret = malloc(sizeof(char) * 40); + _snprintf(ret, sizeof(char) * 40, + "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + return ret; +} + +static cubeb_device_pref +winmm_query_preferred_out_device(UINT devid) +{ + DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status; + cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE; + + if (waveOutMessage((HWAVEOUT)(size_t)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, + (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + devid == mmpref) + ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION; + + if (waveOutMessage((HWAVEOUT)(size_t)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, + (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + devid == compref) + ret |= CUBEB_DEVICE_PREF_VOICE; + + return ret; +} + +static char * +device_id_idx(UINT devid) +{ + char * ret = (char *)malloc(sizeof(char)*16); + _snprintf(ret, 16, "%u", devid); + return ret; +} + +static cubeb_device_info * +winmm_create_device_from_outcaps2(LPWAVEOUTCAPS2A caps, UINT devid) +{ + cubeb_device_info * ret; + + ret = calloc(1, sizeof(cubeb_device_info)); + ret->devid = (cubeb_devid)(size_t)devid; + ret->device_id = device_id_idx(devid); + ret->friendly_name = _strdup(caps->szPname); + ret->group_id = guid_to_cstr(&caps->ProductGuid); + ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid); + + ret->type = CUBEB_DEVICE_TYPE_OUTPUT; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = winmm_query_preferred_out_device(devid); + + ret->max_channels = caps->wChannels; + winmm_calculate_device_rate(ret, caps->dwFormats); + winmm_query_supported_formats(devid, caps->dwFormats, + &ret->format, &ret->default_format); + + /* Hardcoed latency estimates... */ + ret->latency_lo_ms = 100; + ret->latency_hi_ms = 200; + + return ret; +} + +static cubeb_device_info * +winmm_create_device_from_outcaps(LPWAVEOUTCAPSA caps, UINT devid) +{ + cubeb_device_info * ret; + + ret = calloc(1, sizeof(cubeb_device_info)); + ret->devid = (cubeb_devid)(size_t)devid; + ret->device_id = device_id_idx(devid); + ret->friendly_name = _strdup(caps->szPname); + ret->group_id = NULL; + ret->vendor_name = NULL; + + ret->type = CUBEB_DEVICE_TYPE_OUTPUT; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = winmm_query_preferred_out_device(devid); + + ret->max_channels = caps->wChannels; + winmm_calculate_device_rate(ret, caps->dwFormats); + winmm_query_supported_formats(devid, caps->dwFormats, + &ret->format, &ret->default_format); + + /* Hardcoed latency estimates... */ + ret->latency_lo_ms = 100; + ret->latency_hi_ms = 200; + + return ret; +} + +static cubeb_device_pref +winmm_query_preferred_in_device(UINT devid) +{ + DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status; + cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE; + + if (waveInMessage((HWAVEIN)(size_t)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, + (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + devid == mmpref) + ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION; + + if (waveInMessage((HWAVEIN)(size_t)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, + (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + devid == compref) + ret |= CUBEB_DEVICE_PREF_VOICE; + + return ret; +} + +static cubeb_device_info * +winmm_create_device_from_incaps2(LPWAVEINCAPS2A caps, UINT devid) +{ + cubeb_device_info * ret; + + ret = calloc(1, sizeof(cubeb_device_info)); + ret->devid = (cubeb_devid)(size_t)devid; + ret->device_id = device_id_idx(devid); + ret->friendly_name = _strdup(caps->szPname); + ret->group_id = guid_to_cstr(&caps->ProductGuid); + ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid); + + ret->type = CUBEB_DEVICE_TYPE_INPUT; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = winmm_query_preferred_in_device(devid); + + ret->max_channels = caps->wChannels; + winmm_calculate_device_rate(ret, caps->dwFormats); + winmm_query_supported_formats(devid, caps->dwFormats, + &ret->format, &ret->default_format); + + /* Hardcoed latency estimates... */ + ret->latency_lo_ms = 100; + ret->latency_hi_ms = 200; + + return ret; +} + +static cubeb_device_info * +winmm_create_device_from_incaps(LPWAVEINCAPSA caps, UINT devid) +{ + cubeb_device_info * ret; + + ret = calloc(1, sizeof(cubeb_device_info)); + ret->devid = (cubeb_devid)(size_t)devid; + ret->device_id = device_id_idx(devid); + ret->friendly_name = _strdup(caps->szPname); + ret->group_id = NULL; + ret->vendor_name = NULL; + + ret->type = CUBEB_DEVICE_TYPE_INPUT; + ret->state = CUBEB_DEVICE_STATE_ENABLED; + ret->preferred = winmm_query_preferred_in_device(devid); + + ret->max_channels = caps->wChannels; + winmm_calculate_device_rate(ret, caps->dwFormats); + winmm_query_supported_formats(devid, caps->dwFormats, + &ret->format, &ret->default_format); + + /* Hardcoed latency estimates... */ + ret->latency_lo_ms = 100; + ret->latency_hi_ms = 200; + + return ret; +} + +static int +winmm_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection) +{ + UINT i, incount, outcount, total; + cubeb_device_info * cur; + + outcount = waveOutGetNumDevs(); + incount = waveInGetNumDevs(); + total = outcount + incount; + if (total > 0) { + total -= 1; + } + *collection = malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info*) * total); + (*collection)->count = 0; + + if (type & CUBEB_DEVICE_TYPE_OUTPUT) { + WAVEOUTCAPSA woc; + WAVEOUTCAPS2A woc2; + + ZeroMemory(&woc, sizeof(woc)); + ZeroMemory(&woc2, sizeof(woc2)); + + for (i = 0; i < outcount; i++) { + if ((waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR && + (cur = winmm_create_device_from_outcaps2(&woc2, i)) != NULL) || + (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR && + (cur = winmm_create_device_from_outcaps(&woc, i)) != NULL) + ) { + (*collection)->device[(*collection)->count++] = cur; + } + } + } + + if (type & CUBEB_DEVICE_TYPE_INPUT) { + WAVEINCAPSA wic; + WAVEINCAPS2A wic2; + + ZeroMemory(&wic, sizeof(wic)); + ZeroMemory(&wic2, sizeof(wic2)); + + for (i = 0; i < incount; i++) { + if ((waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR && + (cur = winmm_create_device_from_incaps2(&wic2, i)) != NULL) || + (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR && + (cur = winmm_create_device_from_incaps(&wic, i)) != NULL) + ) { + (*collection)->device[(*collection)->count++] = cur; + } + } + } + + return CUBEB_OK; +} + static struct cubeb_ops const winmm_ops = { /*.init =*/ winmm_init, /*.get_backend_id =*/ winmm_get_backend_id, /*.get_max_channel_count=*/ winmm_get_max_channel_count, /*.get_min_latency=*/ winmm_get_min_latency, /*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate, + /*.enumerate_devices =*/ winmm_enumerate_devices, /*.destroy =*/ winmm_destroy, /*.stream_init =*/ winmm_stream_init, /*.stream_destroy =*/ winmm_stream_destroy, diff --git a/media/libcubeb/src/moz.build b/media/libcubeb/src/moz.build index 2a25f6d465..ff001498e8 100644 --- a/media/libcubeb/src/moz.build +++ b/media/libcubeb/src/moz.build @@ -49,6 +49,8 @@ if CONFIG['OS_TARGET'] == 'WINNT': ] DEFINES['USE_WINMM'] = True DEFINES['USE_WASAPI'] = True + if CONFIG['_MSC_VER']: + CXXFLAGS += ['-wd4005'] # C4005: '_USE_MATH_DEFINES' : macro redefinition if CONFIG['OS_TARGET'] == 'Android': SOURCES += ['cubeb_opensl.c'] diff --git a/media/libcubeb/tests/test_audio.cpp b/media/libcubeb/tests/test_audio.cpp index 69c166638b..570c5f1e5d 100644 --- a/media/libcubeb/tests/test_audio.cpp +++ b/media/libcubeb/tests/test_audio.cpp @@ -45,6 +45,8 @@ typedef struct { synth_state* synth_create(int num_channels, float sample_rate) { synth_state* synth = (synth_state *) malloc(sizeof(synth_state)); + if (!synth) + return NULL; for(int i=0;i < MAX_NUM_CHANNELS;++i) synth->phase[i] = 0.0f; synth->num_channels = num_channels; diff --git a/media/libstagefright/binding/BufferStream.cpp b/media/libstagefright/binding/BufferStream.cpp index 5e30ebcc7e..6f6a583980 100644 --- a/media/libstagefright/binding/BufferStream.cpp +++ b/media/libstagefright/binding/BufferStream.cpp @@ -36,7 +36,7 @@ BufferStream::ReadAt(int64_t aOffset, void* aData, size_t aLength, } *aBytesRead = std::min(aLength, size_t(mStartOffset + mData->Length() - aOffset)); - memcpy(aData, &(*mData)[aOffset - mStartOffset], *aBytesRead); + memcpy(aData, mData->Elements() + aOffset - mStartOffset, *aBytesRead); return true; } diff --git a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp index 7b09475bae..37bb2b7a5d 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp @@ -83,7 +83,9 @@ status_t SampleIterator::seekTo(uint32_t sampleIndex) { } } - CHECK(sampleIndex < mStopChunkSampleIndex); + if (sampleIndex >= mStopChunkSampleIndex) { + return ERROR_MALFORMED; + } uint32_t chunk = (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk @@ -116,9 +118,16 @@ status_t SampleIterator::seekTo(uint32_t sampleIndex) { } } + if (mCurrentChunkSampleSizes.size() != mSamplesPerChunk) { + return ERROR_MALFORMED; + } + uint32_t chunkRelativeSampleIndex = (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk; + // This can never happen unless % operator is buggy. + CHECK(chunkRelativeSampleIndex < mSamplesPerChunk); + mCurrentSampleOffset = mCurrentChunkOffset; for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) { mCurrentSampleOffset += mCurrentChunkSampleSizes[i]; @@ -173,7 +182,7 @@ status_t SampleIterator::findChunkRange(uint32_t sampleIndex) { mStopChunkSampleIndex = mFirstChunkSampleIndex + (mStopChunk - mFirstChunk) * mSamplesPerChunk; - } else { + } else if (mSamplesPerChunk) { mStopChunk = 0xffffffff; mStopChunkSampleIndex = 0xffffffff; } diff --git a/mfbt/Assertions.h b/mfbt/Assertions.h index 945f792edb..f138ff9f96 100644 --- a/mfbt/Assertions.h +++ b/mfbt/Assertions.h @@ -21,6 +21,23 @@ #include "nsTraceRefcnt.h" #endif +#if defined(MOZ_CRASHREPORTER) && defined(MOZILLA_INTERNAL_API) && \ + !defined(MOZILLA_EXTERNAL_LINKAGE) && defined(__cplusplus) +namespace CrashReporter { +// This declaration is present here as well as in nsExceptionHandler.h +// nsExceptionHandler.h is not directly included in this file as it includes +// windows.h, which can cause problems when it is imported into some files due +// to the number of macros defined. +// XXX If you change this definition - also change the definition in +// nsExceptionHandler.h +void AnnotateMozCrashReason(const char* aReason); +} // namespace CrashReporter + +# define MOZ_CRASH_ANNOTATE(...) CrashReporter::AnnotateMozCrashReason("" __VA_ARGS__) +#else +# define MOZ_CRASH_ANNOTATE(...) do { /* nothing */ } while (0) +#endif + #include #include #include @@ -249,11 +266,16 @@ __declspec(noreturn) __inline void MOZ_NoReturn() {} * corrupted. */ #ifndef DEBUG -# define MOZ_CRASH(...) MOZ_REALLY_CRASH() +# define MOZ_CRASH(...) \ + do { \ + MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \ + MOZ_REALLY_CRASH(); \ + } while (0) #else # define MOZ_CRASH(...) \ do { \ MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \ + MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \ MOZ_REALLY_CRASH(); \ } while (0) #endif @@ -348,6 +370,7 @@ struct AssertionConditionType MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \ if (MOZ_UNLIKELY(!(expr))) { \ MOZ_ReportAssertionFailure(#expr, __FILE__, __LINE__); \ + MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ")"); \ MOZ_REALLY_CRASH(); \ } \ } while (0) @@ -357,6 +380,7 @@ struct AssertionConditionType MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \ if (MOZ_UNLIKELY(!(expr))) { \ MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \ + MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ") (" explain ")"); \ MOZ_REALLY_CRASH(); \ } \ } while (0) @@ -497,5 +521,6 @@ struct AssertionConditionType #endif #undef MOZ_DUMP_ASSERTION_STACK +#undef MOZ_CRASH_CRASHREPORT #endif /* mozilla_Assertions_h */ diff --git a/mfbt/Constants.h b/mfbt/Constants.h deleted file mode 100644 index 86bbb6b354..0000000000 --- a/mfbt/Constants.h +++ /dev/null @@ -1,16 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* mfbt math constants. */ - -#ifndef mozilla_Constants_h -#define mozilla_Constants_h - -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - -#endif /* mozilla_Constants_h */ diff --git a/mfbt/moz.build b/mfbt/moz.build index b0669e01f5..b716679b18 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -30,7 +30,6 @@ EXPORTS.mozilla = [ 'CheckedInt.h', 'Compiler.h', 'Compression.h', - 'Constants.h', 'DebugOnly.h', 'decimal/Decimal.h', 'double-conversion/double-conversion.h', diff --git a/moz.build b/moz.build index 2b71e3c66f..f35ff1ce24 100644 --- a/moz.build +++ b/moz.build @@ -49,6 +49,9 @@ if not CONFIG['JS_STANDALONE']: 'memory/volatile', ] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android' and int(CONFIG['MOZ_ANDROID_MIN_SDK_VERSION']) < 11: + DIRS += ['other-licenses/android'] + if not CONFIG['JS_STANDALONE']: DIRS += ['xpcom/xpidl'] diff --git a/toolkit/devtools/server/DominatorTree.cpp b/toolkit/devtools/server/DominatorTree.cpp index 242f5292f1..1bc9e23ce0 100644 --- a/toolkit/devtools/server/DominatorTree.cpp +++ b/toolkit/devtools/server/DominatorTree.cpp @@ -4,7 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/devtools/DominatorTree.h" - #include "js/Debug.h" #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/dom/DominatorTreeBinding.h" @@ -12,6 +11,18 @@ namespace mozilla { namespace devtools { +static MallocSizeOf +getCurrentThreadDebuggerMallocSizeOf() +{ + auto ccrt = CycleCollectedJSRuntime::Get(); + MOZ_ASSERT(ccrt); + auto rt = ccrt->Runtime(); + MOZ_ASSERT(rt); + auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt); + MOZ_ASSERT(mallocSizeOf); + return mallocSizeOf; +} + dom::Nullable DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv) { @@ -20,13 +31,7 @@ DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv) if (node.isNothing()) return dom::Nullable(); - auto ccrt = CycleCollectedJSRuntime::Get(); - MOZ_ASSERT(ccrt); - auto rt = ccrt->Runtime(); - MOZ_ASSERT(rt); - auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt); - MOZ_ASSERT(mallocSizeOf); - + auto mallocSizeOf = getCurrentThreadDebuggerMallocSizeOf(); JS::ubi::Node::Size size = 0; if (!mDominatorTree.getRetainedSize(*node, mallocSizeOf, size)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); @@ -38,6 +43,79 @@ DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv) return dom::Nullable(size); } +struct NodeAndRetainedSize +{ + JS::ubi::Node mNode; + JS::ubi::Node::Size mSize; + + NodeAndRetainedSize(const JS::ubi::Node& aNode, JS::ubi::Node::Size aSize) + : mNode(aNode) + , mSize(aSize) + { } + + struct Comparator + { + static bool + Equals(const NodeAndRetainedSize& aLhs, const NodeAndRetainedSize& aRhs) + { + return aLhs.mSize == aRhs.mSize; + } + + static bool + LessThan(const NodeAndRetainedSize& aLhs, const NodeAndRetainedSize& aRhs) + { + // Use > because we want to sort from greatest to least retained size. + return aLhs.mSize > aRhs.mSize; + } + }; +}; + +void +DominatorTree::GetImmediatelyDominated(uint64_t aNodeId, + dom::Nullable>& aOutResult, + ErrorResult& aRv) +{ + MOZ_ASSERT(aOutResult.IsNull()); + + JS::ubi::Node::Id id(aNodeId); + Maybe node = mHeapSnapshot->getNodeById(id); + if (node.isNothing()) + return; + + // Get all immediately dominated nodes and their retained sizes. + MallocSizeOf mallocSizeOf = getCurrentThreadDebuggerMallocSizeOf(); + Maybe range = mDominatorTree.getDominatedSet(*node); + MOZ_ASSERT(range.isSome(), "The node should be known, since we got it from the heap snapshot."); + size_t length = range->length(); + nsTArray dominatedNodes(length); + for (const JS::ubi::Node& dominatedNode : *range) { + JS::ubi::Node::Size retainedSize = 0; + if (NS_WARN_IF(!mDominatorTree.getRetainedSize(dominatedNode, mallocSizeOf, retainedSize))) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + MOZ_ASSERT(retainedSize != 0, + "retainedSize should not be zero since we know the node is in the dominator tree."); + + dominatedNodes.AppendElement(NodeAndRetainedSize(dominatedNode, retainedSize)); + } + + // Sort them by retained size. + NodeAndRetainedSize::Comparator comparator; + dominatedNodes.Sort(comparator); + + // Fill the result with the nodes' ids. + JS::ubi::Node root = mDominatorTree.root(); + aOutResult.SetValue(nsTArray(length)); + for (const NodeAndRetainedSize& entry : dominatedNodes) { + // The root dominates itself, but we don't want to expose that to JS. + if (entry.mNode == root) + continue; + + aOutResult.Value().AppendElement(entry.mNode.identifier()); + } +} + /*** Cycle Collection Boilerplate *****************************************************************/ diff --git a/toolkit/devtools/server/DominatorTree.h b/toolkit/devtools/server/DominatorTree.h index 5cca39e67f..75eec8ad20 100644 --- a/toolkit/devtools/server/DominatorTree.h +++ b/toolkit/devtools/server/DominatorTree.h @@ -52,6 +52,10 @@ public: // [Throws] NodeSize getRetainedSize(NodeId node) dom::Nullable GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv); + + // [Throws] sequence? getImmediatelyDominated(NodeId node); + void GetImmediatelyDominated(uint64_t aNodeId, dom::Nullable>& aOutDominated, + ErrorResult& aRv); }; } // namespace devtools diff --git a/toolkit/devtools/server/tests/unit/test_DominatorTree_05.js b/toolkit/devtools/server/tests/unit/test_DominatorTree_05.js new file mode 100644 index 0000000000..af2f3e2025 --- /dev/null +++ b/toolkit/devtools/server/tests/unit/test_DominatorTree_05.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can get the set of immediately dominated nodes for any given +// node and that this forms a tree. + +function run_test() { + var dominatorTree = saveHeapSnapshotAndComputeDominatorTree(); + equal(typeof dominatorTree.getImmediatelyDominated, "function", + "getImmediatelyDominated should be a function"); + + // Do a traversal of the dominator tree. + // + // Note that we don't assert directly, only if we get an unexpected + // value. There are just way too many nodes in the heap graph to assert for + // every one. This test would constantly time out and assertion messages would + // overflow the log size. + + var root = dominatorTree.root; + var seen = new Set(); + var stack = [root]; + while (stack.length > 0) { + var top = stack.pop(); + + if (seen.has(top)) { + ok(false, + "This is a tree, not a graph: we shouldn't have multiple edges to the same node"); + } + seen.add(top); + if (seen.size % 1000 === 0) { + dumpn("Progress update: seen size = " + seen.size); + } + + var newNodes = dominatorTree.getImmediatelyDominated(top); + if (Object.prototype.toString.call(newNodes) !== "[object Array]") { + ok(false, "getImmediatelyDominated should return an array for known node ids"); + } + + var topSize = dominatorTree.getRetainedSize(top); + + var lastSize = Infinity; + for (var i = 0; i < newNodes.length; i++) { + if (typeof newNodes[i] !== "number") { + ok(false, "Every dominated id should be a number"); + } + + var thisSize = dominatorTree.getRetainedSize(newNodes[i]); + + if (thisSize >= topSize) { + ok(false, "the size of children in the dominator tree should always be less than that of their parent"); + } + + if (thisSize > lastSize) { + ok(false, + "children should be sorted by greatest to least retained size, " + + "lastSize = " + lastSize + ", thisSize = " + thisSize); + } + + lastSize = thisSize; + stack.push(newNodes[i]); + } + } + + ok(true, "Successfully walked the tree"); + dumpn("Walked " + seen.size + " nodes"); + + do_test_finished(); +} diff --git a/widget/windows/nsWinGesture.cpp b/widget/windows/nsWinGesture.cpp index 7ae8aced13..d90110a2f8 100644 --- a/widget/windows/nsWinGesture.cpp +++ b/widget/windows/nsWinGesture.cpp @@ -12,11 +12,12 @@ #include "nsUXThemeData.h" #include "nsIDOMSimpleGestureEvent.h" #include "nsIDOMWheelEvent.h" -#include "mozilla/Constants.h" #include "mozilla/MouseEvents.h" #include "mozilla/Preferences.h" #include "mozilla/TouchEvents.h" +#include + using namespace mozilla; using namespace mozilla::widget; diff --git a/xpcom/ds/nsMathUtils.h b/xpcom/ds/nsMathUtils.h index 8a067ae14e..b10b8144e9 100644 --- a/xpcom/ds/nsMathUtils.h +++ b/xpcom/ds/nsMathUtils.h @@ -7,8 +7,6 @@ #ifndef nsMathUtils_h__ #define nsMathUtils_h__ -#define _USE_MATH_DEFINES /* needed for M_ constants on Win32 */ - #include "nscore.h" #include #include