From 74c60cb62dc70887f27a36a10312228abdfec418 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 3 Oct 2024 21:44:56 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Use CompositorWidgetProxy for dispatching vsync to the compositor. (bug 1269037 part 1, r=mchang) (c8b7a4240c) - Remove unused null widget checks. (bug 1269037 part 2, r=mchang) (4f4cc9952b) - Bug 1269422: Wrap Gonk widget in |CompositorWidgetProxyWrapper|. r=dvander (4513035cbf) - Remove nsBaseWidget::NewCompositorBridgeParent. (bug 1272472 part 1, r=kats) (ca813c1f2b) - Use IPDL to schedule composites on GTK. (bug 1272472 part 2, r=nical) (7402cf834e) - Use IPC to schedule composites on Windows. (bug 1272472 part 3, r=jimm) (e5d7281dd7) - Don't use nsIWidget to check APZ in LayerManagerComposite. (bug 1269653 part 1, r=kats) (79a1644111) - Restrict SetDispAcquireFence's nsIWidget access to Gonk. (bug 1269653 part 2, r=kats) (72110b7bc7) - Bug 1264764 - Move PTexture under PCompositorBridge r=nical,dvander (17e6ec7fc1) - Move CompositorThreadHolder into its own file. (bug 1273017 part 1, r=mattwoodrow) (464ede8be1) - Move CompositorBridgeParent::CompositorLoop to CompositorThreadHolder. (bug 1273017 part 2, r=mattwoodrow) (85708f3cde) - Bug 1268313: Part 1 - Be explicit about which NewRunnableMethod callers want to be able to cancel. r=froydnj (faa07aa139) - Bug 1268313: Part 2 - Replace some NewRunnableMethods with NS_NewNonOwningRunnableMethod. r=froydnj (010c43d000) - Bug 1268313: Part 3 - Replace some NewCancelableRunnableMethod with NS_NewNonOwningCancelableRunnableMethod. r=froydnj (55018ef234) - Bug 1268313: Part 4 - Replace NewCancelableRunnableMethod with NS_NewCancelableRunnableMethod. r=froydnj (c22711b35d) - Bug 1268313: Part 5 - Make NS_NewRunnableMethod able to call const functions. r=froydnj (b0f60963a3) - Bug 1268313: Part 6 - Replace NewRunnableMethod with NS_NewRunnableMethod. r=froydnj (18d40def2c) - Bug 1260950 - Set mInitialSizeFound to true when the initial size id found. r=jesup, r=pehrsons a=kwierso (5d6abe57e0) - Bug 1237176 - Notify synth start if we get a finished event without a blocking-changed event. r=roc (c1aebe903a) - Bug 911546, use a runnable so that popups don't rollup during a grab, r=karlt (a06bd44e6c) - Bug 1237617 - Call nsWindow::ForcePresent during going active r=bas.schouten (066cad8f89) - Bug 1265420 - SetAndFetchFaviconForPage should return a cancelable object to allow aborting the fetch. r=Gijs (a3f7dc54b7) - Bug 1268313: Part 7 - Move NS_NewRunnableMethod and friends to mozilla::NewRunnableMethod. r=froydnj (8b4bf34961) - Bug 1266595: Followup to fix IPDL tests. r=billm (216f2dcff5) - Bug 1268313: Fix up IPDL tests. r=billm (228348d642) - Add WinCompositorWidgetProxy. (bug 1265975 part 1, r=jimm) (bfafe7a8e2) - Implement WinCompositorWidgetProxy::GetClientSize. (bug 1265975 part 2, r=jimm) (a8710a3259) - Move the WM_SETTEXT present lock to CompositorWidgetProxy. (bug 1265975 part 3, r=jimm) (297ce28c8a) - Move transparency handling to WinCompositorWigetProxy. (bug 1265975 part 4, r=jimm) (46ba0c6d01) - Remove Windows-specific compositor calls to nsIWidget. (bug 1265975 part 5, r=jimm) (3ef157c160) - Remove plugin-related CompositorBridgeParent use of nsIWidget. (bug 1265975 part 6, r=jimm) (6d80cdd6fd) - Hide top-level CompositorBridgeParents behind a new API. (bug 1272472 part 4, r=mattwoodrow,kats,gwagner) (228c0efdb7) - Bug 1253424 - part 1 - add a already_AddRefed nsTransactionStack::Push overload; r=erahm (7059e20914) - Bug 1253424 - part 2 - add nsTransactionStack::IsEmpty; r=erahm (7e9764a146) - Bug 1254618 - modify nsTransactionStack to use nsDeque rather than std::deque; r=ehsan (5e47ea431e) - Bug 1136857 - Make DOMStorageCache::mLoaded flag atomic to prevent potential races, r=nfroyd (39aaea1de3) - Bug 1265408 - Add webidl for IIRFilterNode; r=smaug (040ce9aa43) - Bug 1265408 - Implement IIRFilterNode; r=padenot (6bf569a412) - Bug 1265408 - Import IIRFilter from blink; r=padenot (71b28c0ad2) - Bug 1265408 - Use IIRFilter from blink; r=padenot (5d058d8568) - Bug 1265408 - Add buffersAreZero to IIRFilter; r=karlt (45edba3e13) - Bug 1265408 - Avoid subnormals in IIRFilter; r=karlt (0e1ae93f0b) - Bug 1265408 - Add LogToDeveloperConsole to WebAudioUtils; r=padenot (88d5f0222a) - Bug 1268984 - Store GMPStorage on GMPServiceParent so that it persists inside the same PB session. r=gerald (17d4d0abaf) - Bug 1267905 - Replace uses of ScopedCERTCertList with UniqueCERTCertList. r=keeler (783bf11b2a) - Bug 1270005 - Replace uses of ScopedPK11SlotInfo with UniquePK11SlotInfo in PSM. r=keeler (ea9a4011aa) - Bug 1271501 - Remove unnecessary uses of reinterpret_cast in PSM. r=keeler (6be40f0a85) - Bug 1271501 - Downgrade unnecessarily strong reinterpret_casts in PSM. r=keeler (95245f00ce) - Bug 1082346 - 01. Convert PKCS12 password endian using copyAndSwapToBigEndian. r=keeler (9cc58fc550) - Bug 1082346 - 02. Test case. r=keeler r=Cykesiopka (7fb0e8abc4) - Bug 160122 - Stop using PR_smprintf in PSM. r=keeler (1e5b68819c) - Bug 1271501 - Use mozilla::BitwiseCast instead of reinterpret_cast in PSM. r=keeler (894966a2ef) - Bug 1273855: TraceLogger - Include PID in the log names in order to support browser with e10s, r=bbouvier (8cf2233db3) - Bug 1274189. Part 1 - rename some functions to be consistent with other MediaDataDecoder sub-classes. r=jya. (4511b3d3f7) - Bug 1274189. Part 2 - remove use of FlushableTaskQueue::Flush(). r=jya. (77e745fdd1) - Bug 1274189. Part 3 - remove use of FlushableTaskQueue. r=jya (aac61dcd02) - Bug 1269963. Part 1 - Add a SyncRunnable::DispatchToThread() overload for AbstractThread. r=bobbyholley. (839752aff4) - Bug 1269672 - part1 : revert sampling rate changing of the bug1235612. (9015782e13) - Bug 1270698 - check if we need to enter buffering periodically to ensure we start buffering when running out of decoded audio/video data. r=cpearce. (16734549b7) - Bug 1271581 - use newCurrentTime, instead of GetMediaTime() to decide the nextState; r=jwwang (9c5075eada) - Bug 1224973 - Part 1: Remove MediaDecoderOwner->IsHidden(). r=cpearce,jwwang (4fde3ede5a) - Bug 1224973 - Part 2: Set MediaDecoder visibility via NotifyOwnerActivityChanged. r=cpearce,jwwang (be917202eb) - Bug 1224973 - Part 3: Plumb element visibility into MDSM. r=jya,jwwang (9ec83fa243) - Bug 1224973 - Part 4: Pref media.suspend-bkgnd-video.enabled. r=cpearce,jwwang (43413a025f) - Bug 1269408: P1. Retry InternalSeek if previous attempt failed once more data is available. r=gerald (05db58dc7c) - crude fix (0097068989) - Bug 1269408: P2. Update mochitest. r=gerald (464b4c0724) - Bug 1269408: P3. Ensure a new seek request will cancel the previous internal seek. r=gerald (6ed4b8dc95) - Bug 1269408: P4. Ensure the decoders are flushed prior performing an internal seek. r=gerald (074234067b) - Bug 1269408: P5. Only drop the seek target if it's exactly the seek target. r=gerald (88701eb05a) - Bug 1269408: P6. Add debugging information, useful when a mochitest timeout. r=gerald (ef0270ab0d) - Bug 1269408: P7. Start skip to next keyframe logic when resume point is behind current time. r=gerald (bd40ebf3bc) - Bug 1269408: P8. Add debugging log. r=gerald (e6dbd1f0a6) - Bug 1269408: P9. Move handling logic of skip to next keyframe to its own function. r=gerald (3c8039e417) - Bug 1269408: P10. Reject promise early if in error state. r=me (8af54c574e) - Bug 1224973 - Part 5: Implement suspend decoding for background video. r=cpearce,jwwang,jya (22081521e3) - Bug 1242874 - part1 : create suspened types. r=baku (d3ac9548e5) - Bug 1242874 - part2 : window's suspend attribute. r=baku, r=ehsan (1fd9dc2647) - remove allowscirpted (39ab523036) - Bug 1242874 - part3 : implement different suspended methods. r=baku, r=jwwang (25d1f27a03) - Bug 1242874 - part4 : wrap the volume/mute/suspend for notifyStartedPlaying. r=baku (b8ba3238c2) - bug 1242874 - part5 : add test. r=baku, r=ehsan (f840139b5a) - Bug 1235612 - Part 1: Implement notify media-playback. r=baku (b5ec29da20) - Bug 1235612 - Part 2: Notify audible state in NotifyStartedPlaying. r=baku (dc38583a62) - Bug 1235612 - Part 3: Implement the logic of audible state notification for agent owners. r=baku (f65b3952fa) - Bug 1235612 - Part 4: Modify check audible method. r=jwwang (73457e39eb) - Bug 1269672 - part2 : move audible data checking from MDSM to DecodedAudioDataSink. (d2c3b6874c) - Bug 1269936 - Introduce and call a runtime-wide servo initialization hook. r=heycam (d4d505d4c2) - Bug 1263778 - Rename a bunch of low-level [[Prototype]] access methods to make their interactions with statically-known and dynamically-computed [[Prototype]]s clearer. r=efaust (66bbe8e7db) - Bug 888969 - Permit a cyclic [[Prototype]] chain to be created through a Location object. r=bz, r=efaust (3e3b9cbb16) - re-apply Bug 1054906 - Implement ES6 Symbol.hasInstance 2/2; r=jandem (8d5c7573ff) - Bug 1054906 - Implement ES6 Symbol.hasInstance 1/2; r=evilpie,bz (a836904e5d) - fix misspatch (54a5f2d708) - Bug 1270349 part 1. Add IDL parser support for [LegacyUnenumerableNamedProperties]. r=peterv (8c836bc74a) - Bug 1270349 part 2. Add [LegacyUnenumerableNamedProperties] to the interfaces that specify it in DOM and HTML. r=peterv (25d3cc1377) - Bug 1270349 part 3. Add a way to ask an interface descriptor for a proxy whether its named props should be enumerable. r=peterv (0a9f804867) - Bug 1270349 part 4. Use LegacyUnenumerableNamedProperties instead of NameIsEnumerable() calls to determine whether named props on DOM proxies should be enumerable. r=peterv (82f5158963) - Bug 1270349 part 5. Use LegacyUnenumerableNamedProperties instead of passing flags to GetSupportedNames to determine whether named props on DOM proxies should be reflected in ownPropertyKeys. r=peterv (3984176834) - Bug 1270349 followup to address a review comment. r=peterv (b49f4c5335) --- browser/base/content/test/general/audio.ogg | Bin 47411 -> 14293 bytes docshell/base/nsDocShell.cpp | 4 +- docshell/shistory/nsSHistory.cpp | 6 +- dom/animation/Animation.cpp | 2 +- dom/archivereader/ArchiveEvent.cpp | 3 +- dom/audiochannel/AudioChannelAgent.cpp | 94 ++- dom/audiochannel/AudioChannelAgent.h | 6 + dom/audiochannel/AudioChannelService.cpp | 306 +++++++--- dom/audiochannel/AudioChannelService.h | 95 ++- dom/audiochannel/nsIAudioChannelAgent.idl | 91 ++- dom/base/Element.cpp | 2 +- dom/base/EventSource.cpp | 21 +- dom/base/ScreenOrientation.cpp | 4 +- dom/base/WindowNamedPropertiesHandler.cpp | 4 +- dom/base/WindowNamedPropertiesHandler.h | 6 +- dom/base/nsContentList.cpp | 6 +- dom/base/nsContentList.h | 3 +- dom/base/nsContentSink.cpp | 2 +- dom/base/nsContentUtils.cpp | 17 +- dom/base/nsContentUtils.h | 1 + dom/base/nsDOMAttributeMap.cpp | 13 +- dom/base/nsDOMAttributeMap.h | 3 +- dom/base/nsDOMWindowUtils.cpp | 22 +- dom/base/nsDocument.cpp | 20 +- dom/base/nsGlobalWindow.cpp | 65 +- dom/base/nsMimeTypeArray.cpp | 10 +- dom/base/nsMimeTypeArray.h | 3 +- dom/base/nsPIDOMWindow.h | 24 +- dom/base/nsPluginArray.cpp | 16 +- dom/base/nsPluginArray.h | 6 +- dom/base/nsScriptLoader.cpp | 14 +- dom/base/nsTextNode.cpp | 3 +- dom/base/test/audio.ogg | Bin 16521 -> 14293 bytes dom/bindings/Codegen.py | 56 +- dom/bindings/Configuration.py | 20 +- dom/bindings/DOMJSProxyHandler.cpp | 10 + dom/bindings/DOMJSProxyHandler.h | 4 + dom/bindings/parser/WebIDL.py | 21 +- .../tests/test_unenumerable_own_properties.py | 64 ++ dom/bindings/test/TestBindingHeader.h | 27 +- dom/browser-element/mochitest/audio.ogg | Bin 16521 -> 14293 bytes dom/cache/Context.cpp | 9 +- dom/cache/Manager.cpp | 6 +- dom/camera/CameraPreviewMediaStream.cpp | 8 +- dom/camera/DOMCameraCapabilities.cpp | 15 +- dom/camera/DOMCameraCapabilities.h | 3 +- dom/camera/DOMCameraControl.cpp | 6 +- dom/camera/DOMCameraManager.cpp | 6 +- dom/devicestorage/DeviceStorageStatics.cpp | 4 +- dom/events/EventListenerService.cpp | 5 +- dom/fetch/FetchDriver.cpp | 4 +- dom/fmradio/FMRadio.cpp | 24 +- dom/html/HTMLAllCollection.cpp | 6 +- dom/html/HTMLAllCollection.h | 6 +- dom/html/HTMLCanvasElement.cpp | 4 +- dom/html/HTMLFormControlsCollection.cpp | 7 +- dom/html/HTMLFormControlsCollection.h | 3 +- dom/html/HTMLFormElement.cpp | 8 +- dom/html/HTMLFormElement.h | 4 +- dom/html/HTMLImageElement.cpp | 4 +- dom/html/HTMLInputElement.cpp | 2 +- dom/html/HTMLLinkElement.cpp | 4 +- dom/html/HTMLMediaElement.cpp | 314 +++++++--- dom/html/HTMLMediaElement.h | 46 +- dom/html/HTMLObjectElement.cpp | 2 +- dom/html/HTMLOptionsCollection.cpp | 7 +- dom/html/HTMLOptionsCollection.h | 3 +- dom/html/HTMLPropertiesCollection.cpp | 2 +- dom/html/HTMLPropertiesCollection.h | 7 +- dom/html/HTMLSharedObjectElement.cpp | 2 +- dom/html/HTMLStyleElement.cpp | 2 +- dom/html/HTMLTableElement.cpp | 12 +- dom/html/HTMLTrackElement.cpp | 6 +- dom/html/ImageDocument.cpp | 4 +- dom/html/nsDOMStringMap.cpp | 8 +- dom/html/nsDOMStringMap.h | 3 +- dom/html/nsHTMLDocument.cpp | 10 +- dom/html/nsHTMLDocument.h | 3 +- dom/html/nsIHTMLCollection.h | 7 +- dom/indexedDB/ActorsParent.cpp | 6 +- dom/indexedDB/FileSnapshot.cpp | 2 +- dom/indexedDB/IDBDatabase.cpp | 6 +- dom/interfaces/base/nsIDOMWindowUtils.idl | 6 +- dom/ipc/Blob.cpp | 16 +- dom/ipc/ContentBridgeChild.cpp | 3 +- dom/ipc/ContentBridgeParent.cpp | 6 +- dom/ipc/ContentBridgeParent.h | 6 + dom/ipc/ContentChild.cpp | 2 +- dom/ipc/ContentParent.cpp | 17 +- dom/ipc/PreallocatedProcessManager.cpp | 5 +- dom/ipc/ProcessHangMonitor.cpp | 72 +-- dom/jsurl/nsJSProtocolHandler.cpp | 3 +- dom/locales/en-US/chrome/dom/dom.properties | 1 + dom/media/AbstractMediaDecoder.h | 9 +- dom/media/DOMMediaStream.cpp | 8 +- dom/media/MediaDecoder.cpp | 21 +- dom/media/MediaDecoder.h | 16 +- dom/media/MediaDecoderReader.cpp | 20 +- dom/media/MediaDecoderReader.h | 9 +- dom/media/MediaDecoderReaderWrapper.cpp | 10 +- dom/media/MediaDecoderReaderWrapper.h | 3 +- dom/media/MediaDecoderStateMachine.cpp | 206 +++++-- dom/media/MediaDecoderStateMachine.h | 37 +- dom/media/MediaFormatReader.cpp | 260 +++++--- dom/media/MediaFormatReader.h | 83 ++- dom/media/MediaManager.h | 4 +- dom/media/MediaRecorder.cpp | 6 +- dom/media/MediaResource.cpp | 7 +- dom/media/MediaStreamTrack.cpp | 2 +- dom/media/MediaTimer.cpp | 8 +- dom/media/RtspMediaResource.cpp | 4 +- dom/media/SeekJob.cpp | 2 +- dom/media/SeekJob.h | 2 +- dom/media/SeekTarget.h | 11 +- dom/media/SeekTask.cpp | 7 +- dom/media/SeekTask.h | 2 +- dom/media/android/AndroidMediaReader.cpp | 6 +- dom/media/android/AndroidMediaReader.h | 2 +- dom/media/gmp/GMPContentParent.cpp | 4 +- dom/media/gmp/GMPContentParent.h | 2 +- dom/media/gmp/GMPDecryptorChild.cpp | 9 +- dom/media/gmp/GMPDiskStorage.cpp | 499 ++++++++++++++++ dom/media/gmp/GMPMemoryStorage.cpp | 95 +++ dom/media/gmp/GMPParent.cpp | 4 +- dom/media/gmp/GMPPlatform.cpp | 3 +- dom/media/gmp/GMPProcessParent.cpp | 9 +- dom/media/gmp/GMPService.cpp | 11 +- dom/media/gmp/GMPService.h | 1 + dom/media/gmp/GMPServiceParent.cpp | 29 +- dom/media/gmp/GMPServiceParent.h | 6 + dom/media/gmp/GMPStorage.h | 41 ++ dom/media/gmp/GMPStorageChild.cpp | 2 +- dom/media/gmp/GMPStorageParent.cpp | 558 +----------------- dom/media/gmp/GMPStorageParent.h | 19 +- dom/media/gmp/GMPVideoDecoderChild.cpp | 3 +- dom/media/gmp/GMPVideoEncoderChild.cpp | 3 +- dom/media/gmp/moz.build | 3 + dom/media/gtest/GMPTestMonitor.h | 4 +- dom/media/gtest/TestGMPCrossOrigin.cpp | 108 ++-- dom/media/gtest/TestGMPRemoveAndDelete.cpp | 8 +- dom/media/gtest/TestMP4Reader.cpp | 6 +- dom/media/mediasink/DecodedAudioDataSink.cpp | 14 + dom/media/mediasink/DecodedAudioDataSink.h | 11 + dom/media/mediasink/DecodedStream.cpp | 6 +- dom/media/mediasource/MediaSourceDemuxer.cpp | 4 +- dom/media/mediasource/TrackBuffersManager.cpp | 17 +- .../test/test_ResumeAfterClearing_mp4.html | 2 +- dom/media/ogg/OggReader.cpp | 8 +- dom/media/ogg/OggReader.h | 6 +- dom/media/omx/AudioOffloadPlayer.cpp | 17 +- dom/media/omx/MediaOmxCommonDecoder.cpp | 1 + dom/media/platforms/agnostic/OpusDecoder.cpp | 34 +- dom/media/platforms/agnostic/OpusDecoder.h | 12 +- dom/media/platforms/agnostic/VPXDecoder.cpp | 12 +- .../platforms/agnostic/VorbisDecoder.cpp | 12 +- dom/media/platforms/agnostic/WAVDecoder.cpp | 14 +- .../agnostic/gmp/MediaDataDecoderProxy.cpp | 14 +- .../android/AndroidDecoderModule.cpp | 5 +- dom/media/platforms/apple/AppleATDecoder.cpp | 2 +- dom/media/platforms/apple/AppleVDADecoder.cpp | 8 +- dom/media/platforms/apple/AppleVTDecoder.cpp | 2 +- .../platforms/ffmpeg/FFmpegAudioDecoder.cpp | 2 +- .../platforms/ffmpeg/FFmpegDataDecoder.cpp | 6 +- .../platforms/ffmpeg/FFmpegVideoDecoder.cpp | 2 +- dom/media/platforms/omx/OmxDataDecoder.cpp | 16 +- .../platforms/wmf/WMFMediaDataDecoder.cpp | 16 +- .../platforms/wrappers/FuzzingWrapper.cpp | 22 +- dom/media/raw/RawReader.cpp | 4 +- dom/media/raw/RawReader.h | 2 +- .../MediaSystemResourceManager.cpp | 8 +- dom/media/webaudio/AudioContext.cpp | 37 ++ dom/media/webaudio/AudioContext.h | 10 +- dom/media/webaudio/AudioDestinationNode.cpp | 66 +-- dom/media/webaudio/AudioDestinationNode.h | 4 +- dom/media/webaudio/IIRFilterNode.cpp | 225 +++++++ dom/media/webaudio/IIRFilterNode.h | 55 ++ dom/media/webaudio/MediaBufferDecoder.cpp | 2 +- dom/media/webaudio/WebAudioUtils.cpp | 58 ++ dom/media/webaudio/WebAudioUtils.h | 4 + dom/media/webaudio/blink/Biquad.cpp | 1 + dom/media/webaudio/blink/IIRFilter.cpp | 158 +++++ dom/media/webaudio/blink/IIRFilter.h | 58 ++ dom/media/webaudio/blink/ReverbConvolver.cpp | 11 +- dom/media/webaudio/blink/moz.build | 1 + dom/media/webaudio/moz.build | 2 + dom/media/webspeech/synth/nsSpeechTask.cpp | 41 +- .../webspeech/synth/nsSynthVoiceRegistry.cpp | 11 +- .../webspeech/synth/pico/nsPicoService.cpp | 4 +- .../synth/speechd/SpeechDispatcherService.cpp | 10 +- .../synth/test/nsFakeSynthServices.cpp | 2 +- dom/notification/Notification.cpp | 6 +- dom/plugins/base/nsNPAPIPlugin.cpp | 15 +- dom/plugins/base/nsNPAPIPluginInstance.cpp | 11 +- dom/plugins/ipc/PluginInstanceChild.cpp | 16 +- dom/plugins/ipc/PluginInstanceParent.cpp | 9 +- dom/plugins/ipc/PluginModuleChild.cpp | 7 - dom/plugins/ipc/PluginModuleParent.cpp | 8 - dom/plugins/ipc/PluginProcessParent.cpp | 9 +- dom/presentation/PresentationAvailability.cpp | 9 +- .../PresentationDeviceManager.cpp | 2 +- dom/presentation/PresentationSessionInfo.cpp | 12 +- .../PresentationTCPSessionTransport.cpp | 16 +- .../provider/MulticastDNSDeviceProvider.cpp | 2 +- dom/quota/ActorsParent.cpp | 16 +- dom/storage/DOMStorage.cpp | 2 +- dom/storage/DOMStorage.h | 7 +- dom/storage/DOMStorageCache.cpp | 20 +- dom/storage/DOMStorageCache.h | 6 +- dom/storage/DOMStorageDBThread.cpp | 2 +- dom/svg/SVGFEImageElement.cpp | 2 +- dom/svg/SVGImageElement.cpp | 2 +- dom/svg/SVGStyleElement.cpp | 2 +- dom/telephony/Telephony.cpp | 22 +- .../mochitest/general/test_interfaces.html | 2 + dom/tv/TVSource.cpp | 18 +- dom/tv/TVTuner.cpp | 6 +- dom/webidl/AudioContext.webidl | 2 + dom/webidl/HTMLAllCollection.webidl | 1 + dom/webidl/HTMLCollection.webidl | 1 + dom/webidl/HTMLFormElement.webidl | 2 + dom/webidl/HTMLMediaElement.webidl | 2 + dom/webidl/IIRFilterNode.webidl | 17 + dom/webidl/Location.webidl | 2 +- dom/webidl/MimeTypeArray.webidl | 3 + dom/webidl/NamedNodeMap.webidl | 1 + dom/webidl/Plugin.webidl | 3 + dom/webidl/PluginArray.webidl | 3 + dom/webidl/Window.webidl | 2 +- dom/webidl/moz.build | 1 + dom/workers/ScriptLoader.cpp | 8 +- dom/workers/ServiceWorkerEvents.cpp | 5 +- dom/workers/ServiceWorkerJob.cpp | 2 +- dom/workers/ServiceWorkerPrivate.cpp | 4 +- dom/workers/ServiceWorkerRegistrar.cpp | 6 +- dom/workers/ServiceWorkerRegistrationInfo.cpp | 15 +- dom/workers/ServiceWorkerUpdateJob.cpp | 4 +- dom/xbl/nsBindingManager.cpp | 2 +- .../XMLStylesheetProcessingInstruction.cpp | 2 +- dom/xml/nsXMLContentSink.cpp | 2 +- dom/xml/nsXMLPrettyPrinter.cpp | 2 +- dom/xul/XULDocument.cpp | 4 +- dom/xul/templates/nsXULTemplateBuilder.cpp | 8 +- editor/libeditor/nsHTMLEditRules.cpp | 2 +- editor/libeditor/nsHTMLEditor.cpp | 4 +- editor/txmgr/nsTransactionItem.cpp | 4 +- editor/txmgr/nsTransactionManager.cpp | 34 +- editor/txmgr/nsTransactionStack.cpp | 73 ++- editor/txmgr/nsTransactionStack.h | 9 +- .../WebBrowserPersistDocumentParent.cpp | 2 +- .../WebBrowserPersistResourcesParent.cpp | 2 +- .../WebBrowserPersistSerializeParent.cpp | 2 +- .../webbrowserpersist/nsWebBrowserPersist.cpp | 8 +- gfx/gl/AndroidSurfaceTexture.cpp | 3 +- gfx/ipc/CompositorSession.cpp | 122 ++++ gfx/ipc/CompositorSession.h | 71 +++ gfx/ipc/moz.build | 5 + gfx/layers/AtomicRefCountedWithFinalize.h | 3 - gfx/layers/Compositor.cpp | 14 +- gfx/layers/Compositor.h | 5 +- gfx/layers/apz/src/APZCTreeManager.cpp | 7 +- gfx/layers/apz/src/AsyncPanZoomController.cpp | 45 +- gfx/layers/apz/src/GestureEventListener.cpp | 7 +- gfx/layers/apz/src/InputQueue.cpp | 7 +- gfx/layers/apz/util/APZCCallbackHelper.cpp | 8 +- gfx/layers/apz/util/APZCCallbackHelper.h | 6 +- gfx/layers/apz/util/ActiveElementManager.cpp | 6 +- .../apz/util/ChromeProcessController.cpp | 47 +- .../composite/LayerManagerComposite.cpp | 8 +- gfx/layers/composite/TextureHost.cpp | 17 +- gfx/layers/composite/TextureHost.h | 3 +- gfx/layers/d3d11/CompositorD3D11.cpp | 3 +- gfx/layers/d3d9/CompositorD3D9.cpp | 8 +- gfx/layers/ipc/CompositorBridgeChild.cpp | 28 +- gfx/layers/ipc/CompositorBridgeChild.h | 7 + gfx/layers/ipc/CompositorBridgeParent.cpp | 347 ++++++----- gfx/layers/ipc/CompositorBridgeParent.h | 99 ++-- gfx/layers/ipc/CompositorThread.cpp | 146 +++++ gfx/layers/ipc/CompositorThread.h | 65 ++ gfx/layers/ipc/ImageBridgeChild.cpp | 4 +- gfx/layers/ipc/ImageBridgeParent.cpp | 6 +- gfx/layers/ipc/ImageBridgeParent.h | 2 +- gfx/layers/ipc/LayerTransactionChild.cpp | 24 - gfx/layers/ipc/LayerTransactionChild.h | 5 - gfx/layers/ipc/LayerTransactionParent.cpp | 35 -- gfx/layers/ipc/LayerTransactionParent.h | 5 - gfx/layers/ipc/PCompositorBridge.ipdl | 7 + gfx/layers/ipc/PLayerTransaction.ipdl | 3 - gfx/layers/ipc/PTexture.ipdl | 3 +- gfx/layers/ipc/RemoteContentController.cpp | 74 ++- gfx/layers/ipc/ShadowLayers.cpp | 6 +- gfx/layers/moz.build | 2 + gfx/thebes/SoftwareVsyncSource.cpp | 17 +- gfx/thebes/gfxPlatform.cpp | 6 +- gfx/thebes/gfxWindowsPlatform.cpp | 2 +- gfx/vr/ipc/VRManagerChild.cpp | 4 +- gfx/vr/ipc/VRManagerParent.cpp | 20 +- gfx/vr/ipc/VRManagerParent.h | 2 +- image/DecodePool.cpp | 4 +- image/RasterImage.cpp | 4 +- image/VectorImage.cpp | 4 +- image/imgRequestProxy.cpp | 4 +- ipc/chromium/src/base/task.h | 21 +- ipc/glue/BackgroundImpl.cpp | 25 +- ipc/glue/GeckoChildProcessHost.cpp | 29 +- ipc/glue/MessageChannel.cpp | 28 +- ipc/glue/MessageLink.cpp | 27 +- ipc/ipdl/test/cxx/IPDLUnitTests.h | 2 +- ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp | 15 +- ipc/ipdl/test/cxx/TestActorPunning.cpp | 3 +- ipc/ipdl/test/cxx/TestBridgeMain.cpp | 28 +- ipc/ipdl/test/cxx/TestCancel.cpp | 9 +- ipc/ipdl/test/cxx/TestCrashCleanup.cpp | 5 +- ipc/ipdl/test/cxx/TestDemon.cpp | 20 +- ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp | 28 +- ipc/ipdl/test/cxx/TestEndpointOpens.cpp | 30 +- ipc/ipdl/test/cxx/TestHangs.cpp | 9 +- .../test/cxx/TestInterruptErrorCleanup.cpp | 5 +- ipc/ipdl/test/cxx/TestInterruptRaces.cpp | 17 +- .../test/cxx/TestInterruptShutdownRace.cpp | 19 +- ipc/ipdl/test/cxx/TestNestedLoops.cpp | 10 +- ipc/ipdl/test/cxx/TestOpens.cpp | 30 +- ipc/ipdl/test/cxx/TestRaceDeadlock.cpp | 1 + ipc/ipdl/test/cxx/TestRaceDeferral.cpp | 1 + ipc/ipdl/test/cxx/TestStackHooks.cpp | 3 +- ipc/ipdl/test/cxx/TestSyncHang.cpp | 4 +- ipc/ipdl/test/cxx/TestUrgency.cpp | 7 - ipc/ipdl/test/cxx/TestUrgentHangs.cpp | 16 +- js/public/Proxy.h | 2 +- js/src/builtin/MapObject.cpp | 2 +- js/src/builtin/Object.cpp | 2 +- js/src/builtin/RegExp.cpp | 4 +- js/src/builtin/TypedObject.cpp | 10 +- js/src/builtin/TypedObject.h | 3 +- js/src/jit/BaselineIC.cpp | 22 +- js/src/jit/CacheIR.cpp | 14 +- js/src/jit/IonBuilder.cpp | 17 +- js/src/jit/IonCaches.cpp | 43 +- js/src/jit/MCallOptimize.cpp | 2 +- js/src/jit/MIR.cpp | 6 +- js/src/jit/SharedIC.cpp | 31 +- js/src/jsapi.cpp | 2 +- js/src/jsapi.h | 2 +- js/src/jsarray.cpp | 31 +- js/src/jscompartment.cpp | 2 +- js/src/jsfriendapi.cpp | 10 +- js/src/jsfriendapi.h | 3 + js/src/jsfun.cpp | 35 +- js/src/jsiter.cpp | 29 +- js/src/jsobj.cpp | 47 +- js/src/jsobj.h | 78 +-- js/src/jsobjinlines.h | 18 +- js/src/proxy/BaseProxyHandler.cpp | 11 +- js/src/proxy/Proxy.cpp | 7 +- .../ecma_6/Function/has-instance-jitted.js | 96 +++ js/src/tests/ecma_6/Symbol/well-known.js | 1 + js/src/vm/Interpreter.cpp | 12 +- js/src/vm/NativeObject-inl.h | 2 +- js/src/vm/NativeObject.cpp | 13 +- js/src/vm/ObjectGroup.cpp | 8 +- js/src/vm/ObjectGroup.h | 4 + js/src/vm/PIC.cpp | 9 +- js/src/vm/RegExpObject.cpp | 2 +- js/src/vm/ScopeObject.cpp | 6 + js/src/vm/ScopeObject.h | 10 +- js/src/vm/SelfHosting.cpp | 2 +- js/src/vm/Shape-inl.h | 2 +- js/src/vm/Shape.cpp | 2 +- js/src/vm/TaggedProto.cpp | 2 +- js/src/vm/TaggedProto.h | 4 +- js/src/vm/TraceLoggingGraph.cpp | 42 +- js/src/vm/TraceLoggingGraph.h | 7 +- js/src/vm/TypeInference.cpp | 21 +- js/src/vm/UnboxedObject.cpp | 12 +- js/xpconnect/tests/chrome/test_xrayToJS.xul | 4 +- layout/base/ZoomConstraintsClient.cpp | 2 +- layout/base/nsPresContext.cpp | 10 +- layout/base/nsPresShell.cpp | 6 +- layout/base/nsRefreshDriver.cpp | 8 +- layout/build/nsLayoutStatics.cpp | 5 + layout/ipc/VsyncParent.cpp | 6 +- layout/style/FontFaceSet.cpp | 2 +- layout/style/ServoBindings.cpp | 7 + layout/style/ServoBindings.h | 5 +- layout/xul/tree/nsTreeColumns.cpp | 8 +- layout/xul/tree/nsTreeColumns.h | 3 +- .../src/mediapipeline/MediaPipeline.cpp | 2 +- modules/libjar/nsJARChannel.cpp | 9 +- modules/libpref/init/all.js | 2 - netwerk/base/BackgroundFileSaver.cpp | 22 +- netwerk/base/Dashboard.cpp | 77 ++- netwerk/base/OfflineObserver.cpp | 10 +- netwerk/base/Tickler.cpp | 4 +- netwerk/base/nsAsyncStreamCopier.cpp | 5 +- netwerk/base/nsInputStreamPump.cpp | 2 +- netwerk/base/nsPACMan.cpp | 8 +- netwerk/base/nsServerSocket.cpp | 7 +- netwerk/base/nsSocketTransportService2.cpp | 8 +- netwerk/base/nsUDPSocket.cpp | 6 +- netwerk/cache/nsCacheService.cpp | 4 +- netwerk/cache2/CacheEntry.cpp | 11 +- netwerk/cache2/CacheFileChunk.cpp | 4 +- netwerk/cache2/CacheFileContextEvictor.cpp | 2 +- netwerk/cache2/CacheFileIOManager.cpp | 25 +- netwerk/cache2/CacheIndex.cpp | 2 +- netwerk/cache2/CacheObserver.cpp | 2 +- netwerk/cache2/CacheStorageService.cpp | 6 +- netwerk/dns/DNSRequestChild.cpp | 4 +- .../mdns/libmdns/MDNSResponderOperator.cpp | 11 +- netwerk/ipc/ChannelEventQueue.cpp | 8 +- netwerk/protocol/about/nsAboutCache.cpp | 4 +- netwerk/protocol/file/nsFileChannel.cpp | 2 +- netwerk/protocol/ftp/FTPChannelParent.cpp | 2 +- netwerk/protocol/http/HttpBaseChannel.cpp | 2 +- netwerk/protocol/http/HttpBaseChannel.h | 4 +- netwerk/protocol/http/HttpChannelParent.cpp | 2 +- netwerk/protocol/http/PackagedAppVerifier.cpp | 12 +- netwerk/protocol/http/nsHttpHandler.cpp | 4 +- .../protocol/websocket/WebSocketChannel.cpp | 16 +- .../websocket/WebSocketChannelChild.cpp | 6 +- netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp | 2 +- parser/html/nsHtml5TreeOpExecutor.cpp | 7 +- security/apps/AppSignatureVerification.cpp | 15 +- security/apps/AppTrustDomain.cpp | 16 +- security/apps/AppTrustDomain.h | 10 +- security/certverifier/CertVerifier.cpp | 6 +- security/certverifier/CertVerifier.h | 10 +- .../certverifier/NSSCertDBTrustDomain.cpp | 149 ++--- security/certverifier/NSSCertDBTrustDomain.h | 18 +- security/certverifier/OCSPRequestor.cpp | 17 +- security/manager/ssl/CSTrustDomain.cpp | 2 +- security/manager/ssl/CSTrustDomain.h | 4 +- security/manager/ssl/CertBlocklist.cpp | 57 +- .../manager/ssl/ContentSignatureVerifier.cpp | 12 +- security/manager/ssl/DataStorage.cpp | 16 +- security/manager/ssl/LocalCertService.cpp | 45 +- security/manager/ssl/PSMContentListener.cpp | 11 +- .../manager/ssl/PublicKeyPinningService.cpp | 24 +- .../manager/ssl/PublicKeyPinningService.h | 9 +- .../ssl/RootCertificateTelemetryUtils.cpp | 6 +- .../manager/ssl/SSLServerCertVerification.cpp | 55 +- security/manager/ssl/ScopedNSSTypes.h | 22 +- .../manager/ssl/TransportSecurityInfo.cpp | 34 +- security/manager/ssl/TransportSecurityInfo.h | 10 +- .../manager/ssl/nsCertOverrideService.cpp | 21 +- security/manager/ssl/nsCertPicker.cpp | 10 +- security/manager/ssl/nsCertTree.cpp | 10 +- .../manager/ssl/nsDataSignatureVerifier.cpp | 12 +- security/manager/ssl/nsIX509CertList.idl | 13 +- security/manager/ssl/nsKeyModule.cpp | 2 +- security/manager/ssl/nsNSSCallbacks.cpp | 32 +- security/manager/ssl/nsNSSCallbacks.h | 27 +- security/manager/ssl/nsNSSCertHelper.cpp | 11 +- security/manager/ssl/nsNSSCertHelper.h | 11 +- security/manager/ssl/nsNSSCertificate.cpp | 86 +-- security/manager/ssl/nsNSSCertificate.h | 37 +- security/manager/ssl/nsNSSCertificateDB.cpp | 103 ++-- security/manager/ssl/nsNSSCertificateDB.h | 3 +- .../ssl/nsNSSCertificateFakeTransport.cpp | 2 +- security/manager/ssl/nsNSSComponent.cpp | 3 +- security/manager/ssl/nsNSSIOLayer.cpp | 24 +- security/manager/ssl/nsNSSShutDown.cpp | 5 +- security/manager/ssl/nsNSSU2FToken.cpp | 55 +- security/manager/ssl/nsNSSU2FToken.h | 2 +- security/manager/ssl/nsNTLMAuthModule.cpp | 76 +-- security/manager/ssl/nsPK11TokenDB.cpp | 9 +- security/manager/ssl/nsPKCS11Slot.cpp | 7 +- security/manager/ssl/nsPKCS12Blob.cpp | 75 ++- security/manager/ssl/nsPKCS12Blob.h | 2 +- security/manager/ssl/nsRandomGenerator.cpp | 6 +- .../manager/ssl/nsSiteSecurityService.cpp | 2 +- security/manager/ssl/nsSmartCardMonitor.cpp | 22 +- security/manager/ssl/nsUsageArrayHelper.cpp | 3 +- .../manager/ssl/tests/compiled/TestMD4.cpp | 4 +- .../manager/ssl/tests/gtest/OCSPCacheTest.cpp | 16 +- .../test_certDB_import/cert_from_windows.pfx | Bin 0 -> 2041 bytes .../tests/unit/test_certDB_import_pkcs12.js | 92 +++ .../tests/unit/tlsserver/lib/TLSServer.cpp | 25 +- .../ssl/tests/unit/tlsserver/lib/TLSServer.h | 6 +- security/manager/ssl/tests/unit/xpcshell.ini | 1 + storage/mozStorageConnection.cpp | 2 +- storage/mozStorageService.cpp | 4 +- testing/web-platform/meta/MANIFEST.json | 6 + ..._prototype_cycle_through_location.sub.html | 197 +++++++ .../cross_origin_joined_frame.sub.html | 15 + .../same_origin_frame.html | 12 + .../filewatcher/NativeFileWatcherWin.cpp | 10 +- .../components/places/AsyncFaviconHelpers.cpp | 140 +++-- .../components/places/AsyncFaviconHelpers.h | 80 +-- toolkit/components/places/History.cpp | 4 +- toolkit/components/places/moz.build | 1 + .../components/places/mozIAsyncFavicons.idl | 15 +- .../places/mozIPlacesPendingOperation.idl | 14 + .../components/places/nsFaviconService.cpp | 7 +- .../satchel/nsFormFillController.cpp | 2 +- toolkit/components/telemetry/Telemetry.cpp | 2 +- .../url-classifier/nsUrlClassifierProxies.cpp | 25 +- toolkit/content/browser-content.js | 63 +- toolkit/content/tests/browser/audio.ogg | Bin 16521 -> 14290 bytes toolkit/content/tests/browser/browser.ini | 3 + .../browser_mediaPlayback_suspended.js | 164 +++++ ...r_mediaPlayback_suspended_multipleAudio.js | 305 ++++++++++ .../tests/browser/file_multipleAudio.html | 7 + toolkit/content/tests/widgets/audio.ogg | Bin 47411 -> 14293 bytes toolkit/content/widgets/browser.xml | 48 +- toolkit/xre/nsEmbedFunctions.cpp | 7 - toolkit/xre/nsUpdateDriver.cpp | 10 +- uriloader/prefetch/nsOfflineCacheUpdate.cpp | 6 +- widget/CompositorWidgetProxy.cpp | 58 +- widget/CompositorWidgetProxy.h | 27 + widget/ScreenProxy.cpp | 4 +- widget/VsyncDispatcher.cpp | 7 +- widget/gonk/nsWindow.cpp | 21 +- widget/gonk/nsWindow.h | 7 +- widget/gtk/nsDeviceContextSpecG.cpp | 8 +- widget/gtk/nsWindow.cpp | 20 +- widget/gtk/nsWindow.h | 5 + widget/nsBaseWidget.cpp | 98 +-- widget/nsBaseWidget.h | 13 +- widget/nsIWidget.h | 7 - widget/nsScreenManagerProxy.cpp | 4 +- widget/qt/nsWindow.h | 2 +- widget/windows/AudioSession.cpp | 2 +- widget/windows/LSPAnnotator.cpp | 2 +- widget/windows/WinCompositorWidgetProxy.cpp | 255 ++++++++ widget/windows/WinCompositorWidgetProxy.h | 83 +++ .../WinTextEventDispatcherListener.cpp | 1 + widget/windows/moz.build | 2 + widget/windows/nsWindow.cpp | 220 ++----- widget/windows/nsWindow.h | 22 +- widget/windows/nsWindowGfx.cpp | 24 +- xpcom/base/nsDumpUtils.cpp | 3 +- xpcom/base/nsDumpUtils.h | 3 +- xpcom/base/nsMemoryReporterManager.cpp | 5 +- xpcom/ds/nsObserverService.cpp | 4 +- xpcom/glue/nsThreadUtils.h | 158 +++-- xpcom/glue/tests/gtest/TestThreadUtils.cpp | 82 +-- xpcom/tests/TestThreadUtils.cpp | 32 +- xpcom/threads/AbstractThread.cpp | 10 +- xpcom/threads/AbstractThread.h | 1 + xpcom/threads/LazyIdleThread.cpp | 8 +- xpcom/threads/MozPromise.h | 2 +- xpcom/threads/SharedThreadPool.cpp | 5 +- xpcom/threads/StateMirroring.h | 17 +- xpcom/threads/StateWatching.h | 3 +- xpcom/threads/SyncRunnable.h | 27 + xpcom/threads/nsProcessCommon.cpp | 4 +- xpcom/threads/nsThreadPool.cpp | 5 +- 547 files changed, 7623 insertions(+), 4417 deletions(-) create mode 100644 dom/bindings/parser/tests/test_unenumerable_own_properties.py create mode 100644 dom/media/gmp/GMPDiskStorage.cpp create mode 100644 dom/media/gmp/GMPMemoryStorage.cpp create mode 100644 dom/media/gmp/GMPStorage.h create mode 100644 dom/media/webaudio/IIRFilterNode.cpp create mode 100644 dom/media/webaudio/IIRFilterNode.h create mode 100644 dom/media/webaudio/blink/IIRFilter.cpp create mode 100644 dom/media/webaudio/blink/IIRFilter.h create mode 100644 dom/webidl/IIRFilterNode.webidl create mode 100644 gfx/ipc/CompositorSession.cpp create mode 100644 gfx/ipc/CompositorSession.h create mode 100644 gfx/layers/ipc/CompositorThread.cpp create mode 100644 gfx/layers/ipc/CompositorThread.h create mode 100644 js/src/tests/ecma_6/Function/has-instance-jitted.js create mode 100644 security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows.pfx create mode 100644 security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js create mode 100644 testing/web-platform/tests/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html create mode 100644 testing/web-platform/tests/html/browsers/history/the-location-interface/cross_origin_joined_frame.sub.html create mode 100644 testing/web-platform/tests/html/browsers/history/the-location-interface/same_origin_frame.html create mode 100644 toolkit/components/places/mozIPlacesPendingOperation.idl create mode 100644 toolkit/content/tests/browser/browser_mediaPlayback_suspended.js create mode 100644 toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js create mode 100644 toolkit/content/tests/browser/file_multipleAudio.html create mode 100644 widget/windows/WinCompositorWidgetProxy.cpp create mode 100644 widget/windows/WinCompositorWidgetProxy.h diff --git a/browser/base/content/test/general/audio.ogg b/browser/base/content/test/general/audio.ogg index 7e6ef77ec407eccb3e07bc21d85d8418ab84e011..477544875d28a2005ae730b5caced8c24ae12631 100644 GIT binary patch literal 14293 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 Twi=WXNG`ig0w-tn#RC5e4hMHM literal 47411 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 L8KN)BbN&ASz3Fi! diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index e391a748ea..3005773988 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -134,6 +134,7 @@ #include "nsIStructuredCloneContainer.h" #ifdef MOZ_PLACES #include "nsIFaviconService.h" +#include "mozIPlacesPendingOperation.h" #include "mozIAsyncFavicons.h" #endif #include "nsINetworkPredictor.h" @@ -9437,11 +9438,12 @@ public: MOZ_ASSERT(aDataLen == 0, "We weren't expecting the callback to deliver data."); + nsCOMPtr po; return mSvc->SetAndFetchFaviconForPage( mNewURI, aFaviconURI, false, mInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE, - nullptr, mLoadingPrincipal); + nullptr, mLoadingPrincipal, getter_AddRefs(po)); } private: diff --git a/docshell/shistory/nsSHistory.cpp b/docshell/shistory/nsSHistory.cpp index 5bfac6bc9f..ff81a3a3ab 100644 --- a/docshell/shistory/nsSHistory.cpp +++ b/docshell/shistory/nsSHistory.cpp @@ -1383,10 +1383,8 @@ nsSHistory::RemoveEntries(nsTArray& aIDs, int32_t aStartIndex) --index; } if (didRemove && mRootDocShell) { - nsCOMPtr ev = - NS_NewRunnableMethod(static_cast(mRootDocShell), - &nsDocShell::FireDummyOnLocationChange); - NS_DispatchToCurrentThread(ev); + NS_DispatchToCurrentThread(NewRunnableMethod(static_cast(mRootDocShell), + &nsDocShell::FireDummyOnLocationChange)); } } diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp index c4299a19f0..c24fe0c99a 100644 --- a/dom/animation/Animation.cpp +++ b/dom/animation/Animation.cpp @@ -1236,7 +1236,7 @@ Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag) DoFinishNotificationImmediately(); } else if (!mFinishNotificationTask.IsPending()) { RefPtr> runnable = - NS_NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately); + NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately); runtime->DispatchToMicroTask(runnable); mFinishNotificationTask = runnable; } diff --git a/dom/archivereader/ArchiveEvent.cpp b/dom/archivereader/ArchiveEvent.cpp index 164a03eb2d..b1b3dcd823 100644 --- a/dom/archivereader/ArchiveEvent.cpp +++ b/dom/archivereader/ArchiveEvent.cpp @@ -86,8 +86,7 @@ ArchiveReaderEvent::RunShare(nsresult aStatus) { mStatus = aStatus; - nsCOMPtr event = NS_NewRunnableMethod(this, &ArchiveReaderEvent::ShareMainThread); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(this, &ArchiveReaderEvent::ShareMainThread)); return NS_OK; } diff --git a/dom/audiochannel/AudioChannelAgent.cpp b/dom/audiochannel/AudioChannelAgent.cpp index 357cb311c2..95f2f2d971 100644 --- a/dom/audiochannel/AudioChannelAgent.cpp +++ b/dom/audiochannel/AudioChannelAgent.cpp @@ -201,17 +201,12 @@ AudioChannelAgent::InitInternal(nsPIDOMWindowInner* aWindow, return NS_OK; } -NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(float *aVolume, - bool* aMuted) +NS_IMETHODIMP +AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig, + bool aAudible) { - MOZ_ASSERT(aVolume); - MOZ_ASSERT(aMuted); - - // Window-less AudioChannelAgents are muted by default. - if (!mWindow) { - *aVolume = 0; - *aMuted = true; - return NS_OK; + if (NS_WARN_IF(!aConfig)) { + return NS_ERROR_FAILURE; } RefPtr service = AudioChannelService::GetOrCreate(); @@ -220,20 +215,26 @@ NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(float *aVolume, return NS_ERROR_FAILURE; } + MOZ_ASSERT(AudioChannelService::AudibleState::eAudible == true && + AudioChannelService::AudibleState::eNotAudible == false); service->RegisterAudioChannelAgent(this, - static_cast(mAudioChannelType)); + static_cast(aAudible)); - service->GetState(mWindow, mAudioChannelType, aVolume, aMuted); + AudioPlaybackConfig config = service->GetMediaConfig(mWindow, + mAudioChannelType); MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, - ("AudioChannelAgent, NotifyStartedPlaying, this = %p, mute = %d, " - "volume = %f\n", this, *aMuted, *aVolume)); + ("AudioChannelAgent, NotifyStartedPlaying, this = %p, " + "mute = %d, volume = %f, suspend = %d\n", this, + config.mMuted, config.mVolume, config.mSuspend)); + aConfig->SetConfig(config.mVolume, config.mMuted, config.mSuspend); mIsRegToService = true; return NS_OK; } -NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying() +NS_IMETHODIMP +AudioChannelAgent::NotifyStoppedPlaying() { if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR || !mIsRegToService) { @@ -252,6 +253,23 @@ NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying() return NS_OK; } +NS_IMETHODIMP +AudioChannelAgent::NotifyStartedAudible(bool aAudible) +{ + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("AudioChannelAgent, NotifyStartedAudible, this = %p, " + "audible = %d\n", this, aAudible)); + + RefPtr service = AudioChannelService::GetOrCreate(); + if (NS_WARN_IF(!service)) { + return NS_ERROR_FAILURE; + } + + service->AudioAudibleChanged( + this, static_cast(aAudible)); + return NS_OK; +} + already_AddRefed AudioChannelAgent::GetCallback() { @@ -270,19 +288,49 @@ AudioChannelAgent::WindowVolumeChanged() return; } - float volume = 1.0; - bool muted = false; + AudioPlaybackConfig config = GetMediaConfig(); + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, " + "volume = %f\n", this, config.mMuted, config.mVolume)); - RefPtr service = AudioChannelService::GetOrCreate(); - if (service) { - service->GetState(mWindow, mAudioChannelType, &volume, &muted); + callback->WindowVolumeChanged(config.mVolume, config.mMuted); +} + +void +AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend) +{ + nsCOMPtr callback = GetCallback(); + if (!callback) { + return; + } + + if (!IsDisposableSuspend(aSuspend)) { + aSuspend = GetMediaConfig().mSuspend; } MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, - ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, " - "volume = %f\n", this, muted, volume)); + ("AudioChannelAgent, WindowSuspendChanged, this = %p, " + "suspended = %d\n", this, aSuspend)); - callback->WindowVolumeChanged(volume, muted); + callback->WindowSuspendChanged(aSuspend); +} + +AudioPlaybackConfig +AudioChannelAgent::GetMediaConfig() +{ + RefPtr service = AudioChannelService::GetOrCreate(); + AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED); + if (service) { + config = service->GetMediaConfig(mWindow, mAudioChannelType); + } + return config; +} + +bool +AudioChannelAgent::IsDisposableSuspend(nsSuspendedTypes aSuspend) const +{ + return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE || + aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE); } uint64_t diff --git a/dom/audiochannel/AudioChannelAgent.h b/dom/audiochannel/AudioChannelAgent.h index 7c87474d55..10cbec1ac0 100644 --- a/dom/audiochannel/AudioChannelAgent.h +++ b/dom/audiochannel/AudioChannelAgent.h @@ -23,6 +23,8 @@ class nsPIDOMWindowOuter; namespace mozilla { namespace dom { +class AudioPlaybackConfig; + /* Header file */ class AudioChannelAgent : public nsIAudioChannelAgent { @@ -35,6 +37,7 @@ public: AudioChannelAgent(); void WindowVolumeChanged(); + void WindowSuspendChanged(nsSuspendedTypes aSuspend); void WindowAudioCaptureChanged(uint64_t aInnerWindowID, bool aCapture); nsPIDOMWindowOuter* Window() const @@ -48,6 +51,9 @@ public: private: virtual ~AudioChannelAgent(); + AudioPlaybackConfig GetMediaConfig(); + bool IsDisposableSuspend(nsSuspendedTypes aSuspend) const; + // Returns mCallback if that's non-null, or otherwise tries to get an // nsIAudioChannelAgentCallback out of mWeakCallback. already_AddRefed GetCallback(); diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index 667665c375..7a06281bff 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -94,25 +94,16 @@ private: const bool mActive; }; -void -NotifyChannelActive(uint64_t aWindowID, AudioChannel aAudioChannel, - bool aActive) -{ - RefPtr runnable = - new NotifyChannelActiveRunnable(aWindowID, aAudioChannel, aActive); - NS_DispatchToCurrentThread(runnable); -} - bool IsParentProcess() { return XRE_GetProcessType() == GeckoProcessType_Default; } -class MediaPlaybackRunnable : public Runnable +class AudioPlaybackRunnable final : public Runnable { public: - MediaPlaybackRunnable(nsPIDOMWindowOuter* aWindow, bool aActive) + AudioPlaybackRunnable(nsPIDOMWindowOuter* aWindow, bool aActive) : mWindow(aWindow) , mActive(aActive) {} @@ -121,14 +112,17 @@ public: { nsCOMPtr observerService = services::GetObserverService(); - if (observerService) { - observerService->NotifyObservers( - ToSupports(mWindow), - "audio-playback", - mActive ? MOZ_UTF16("active") - : MOZ_UTF16("inactive")); + if (NS_WARN_IF(!observerService)) { + return NS_ERROR_FAILURE; } + observerService->NotifyObservers(ToSupports(mWindow), + "audio-playback", + mActive ? MOZ_UTF16("active") + : MOZ_UTF16("inactive")); + + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("AudioPlaybackRunnable, active = %d\n", mActive)); return NS_OK; } @@ -255,8 +249,10 @@ AudioChannelService::~AudioChannelService() void AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent, - AudioChannel aChannel) + AudibleState aAudible) { + MOZ_ASSERT(aAgent); + uint64_t windowID = aAgent->WindowID(); AudioChannelWindow* winData = GetWindowData(windowID); if (!winData) { @@ -264,29 +260,11 @@ AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent, mWindows.AppendElement(winData); } - MOZ_ASSERT(!winData->mAgents.Contains(aAgent)); - winData->mAgents.AppendElement(aAgent); - - ++winData->mChannels[(uint32_t)aChannel].mNumberOfAgents; - - // The first one, we must inform the BrowserElementAudioChannel. - if (winData->mChannels[(uint32_t)aChannel].mNumberOfAgents == 1) { - NotifyChannelActive(aAgent->WindowID(), aChannel, true); - } - - // If this is the first agent for this window, we must notify the observers. - if (winData->mAgents.Length() == 1) { - RefPtr runnable = - new MediaPlaybackRunnable(aAgent->Window(), true /* active */); - NS_DispatchToCurrentThread(runnable); - } - - // If the window has already been captured, the agent of that window should - // also be captured. - if (winData->mIsAudioCaptured) { - aAgent->WindowAudioCaptureChanged(aAgent->InnerWindowID(), - winData->mIsAudioCaptured); - } + // To make sure agent would be alive because AppendAgent() would trigger the + // callback function of AudioChannelAgentOwner that means the agent might be + // released in their callback. + RefPtr kungFuDeathGrip(aAgent); + winData->AppendAgent(aAgent, aAudible); MaybeSendStatusUpdate(); } @@ -294,27 +272,18 @@ AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent, void AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) { + MOZ_ASSERT(aAgent); + AudioChannelWindow* winData = GetWindowData(aAgent->WindowID()); if (!winData) { return; } - if (winData->mAgents.Contains(aAgent)) { - int32_t channel = aAgent->AudioChannelType(); - uint64_t windowID = aAgent->WindowID(); - - // aAgent can be freed after this call. - winData->mAgents.RemoveElement(aAgent); - - MOZ_ASSERT(winData->mChannels[channel].mNumberOfAgents > 0); - - --winData->mChannels[channel].mNumberOfAgents; - - // The last one, we must inform the BrowserElementAudioChannel. - if (winData->mChannels[channel].mNumberOfAgents == 0) { - NotifyChannelActive(windowID, static_cast(channel), false); - } - } + // To make sure agent would be alive because AppendAgent() would trigger the + // callback function of AudioChannelAgentOwner that means the agent might be + // released in their callback. + RefPtr kungFuDeathGrip(aAgent); + winData->RemoveAgent(aAgent); #ifdef MOZ_WIDGET_GONK bool active = AnyAudioChannelIsActive(); @@ -323,18 +292,6 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) } #endif - // If this is the last agent for this window, we must notify the observers. - if (winData->mAgents.IsEmpty()) { - RefPtr runnable = - new MediaPlaybackRunnable(aAgent->Window(), false /* active */); - NS_DispatchToCurrentThread(runnable); - } - - // No need to capture non-audible object. - if (winData->mIsAudioCaptured) { - aAgent->WindowAudioCaptureChanged(aAgent->InnerWindowID(), false); - } - MaybeSendStatusUpdate(); } @@ -353,24 +310,22 @@ AudioChannelService::UnregisterTabParent(TabParent* aTabParent) mTabParents.RemoveElement(aTabParent); } -void -AudioChannelService::GetState(nsPIDOMWindowOuter* aWindow, uint32_t aAudioChannel, - float* aVolume, bool* aMuted) +AudioPlaybackConfig +AudioChannelService::GetMediaConfig(nsPIDOMWindowOuter* aWindow, + uint32_t aAudioChannel) const { MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow()); - MOZ_ASSERT(aVolume && aMuted); MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS); + AudioPlaybackConfig config(1.0, false, + nsISuspendedTypes::NONE_SUSPENDED); if (!aWindow || !aWindow->IsOuterWindow()) { - *aVolume = 0.0; - *aMuted = true; - return; + config.SetConfig(0.0, true, + nsISuspendedTypes::SUSPENDED_BLOCK); + return config; } - *aVolume = 1.0; - *aMuted = false; - AudioChannelWindow* winData = nullptr; nsCOMPtr window = aWindow; @@ -379,13 +334,16 @@ AudioChannelService::GetState(nsPIDOMWindowOuter* aWindow, uint32_t aAudioChanne do { winData = GetWindowData(window->WindowID()); if (winData) { - *aVolume *= winData->mChannels[aAudioChannel].mVolume; - *aMuted = *aMuted || winData->mChannels[aAudioChannel].mMuted; + config.mVolume *= winData->mChannels[aAudioChannel].mVolume; + config.mMuted = config.mMuted || winData->mChannels[aAudioChannel].mMuted; } - *aVolume *= window->GetAudioVolume(); - // TODO : distiguish between suspend and mute, it would be done in bug1242874. - *aMuted = *aMuted || window->GetMediaSuspended() || window->GetAudioMuted(); + config.mVolume *= window->GetAudioVolume(); + config.mMuted = config.mMuted || window->GetAudioMuted(); + + // If the mSuspend is already suspended, we don't need to set it again. + config.mSuspend = (config.mSuspend == nsISuspendedTypes::NONE_SUSPENDED) ? + window->GetMediaSuspend() : config.mSuspend; nsCOMPtr win = window->GetScriptableParentOrNull(); if (!win) { @@ -396,6 +354,21 @@ AudioChannelService::GetState(nsPIDOMWindowOuter* aWindow, uint32_t aAudioChanne // If there is no parent, or we are the toplevel we don't continue. } while (window && window != aWindow); + + return config; +} + +void +AudioChannelService::AudioAudibleChanged(AudioChannelAgent* aAgent, + AudibleState aAudible) +{ + MOZ_ASSERT(aAgent); + + uint64_t windowID = aAgent->WindowID(); + AudioChannelWindow* winData = GetWindowData(windowID); + if (winData) { + winData->AudioAudibleChanged(aAgent, aAudible); + } } bool @@ -600,7 +573,8 @@ AudioChannelService::RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel, } void -AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow) +AudioChannelService::RefreshAgents(nsPIDOMWindowOuter* aWindow, + mozilla::function aFunc) { MOZ_ASSERT(aWindow); MOZ_ASSERT(aWindow->IsOuterWindow()); @@ -618,10 +592,27 @@ AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow) nsTObserverArray::ForwardIterator iter(winData->mAgents); while (iter.HasMore()) { - iter.GetNext()->WindowVolumeChanged(); + aFunc(iter.GetNext()); } } +void +AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow) +{ + RefreshAgents(aWindow, [] (AudioChannelAgent* agent) { + agent->WindowVolumeChanged(); + }); +} + +void +AudioChannelService::RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow, + nsSuspendedTypes aSuspend) +{ + RefreshAgents(aWindow, [aSuspend] (AudioChannelAgent* agent) { + agent->WindowSuspendChanged(aSuspend); + }); +} + void AudioChannelService::SetWindowAudioCaptured(nsPIDOMWindowOuter* aWindow, uint64_t aInnerWindowID, @@ -1009,3 +1000,146 @@ AudioChannelService::IsAudioChannelMutedByDefault() CreateServiceIfNeeded(); return sAudioChannelMutedByDefault; } + +void +AudioChannelService::AudioChannelWindow::AppendAgent(AudioChannelAgent* aAgent, + AudibleState aAudible) +{ + MOZ_ASSERT(aAgent); + + AppendAgentAndIncreaseAgentsNum(aAgent); + AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing); + AudioAudibleChanged(aAgent, aAudible); +} + +void +AudioChannelService::AudioChannelWindow::RemoveAgent(AudioChannelAgent* aAgent) +{ + MOZ_ASSERT(aAgent); + + RemoveAgentAndReduceAgentsNum(aAgent); + AudioCapturedChanged(aAgent, AudioCaptureState::eNotCapturing); + AudioAudibleChanged(aAgent, AudibleState::eNotAudible); +} + +void +AudioChannelService::AudioChannelWindow::AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent) +{ + MOZ_ASSERT(aAgent); + MOZ_ASSERT(!mAgents.Contains(aAgent)); + + int32_t channel = aAgent->AudioChannelType(); + mAgents.AppendElement(aAgent); + + ++mChannels[channel].mNumberOfAgents; + + // The first one, we must inform the BrowserElementAudioChannel. + if (mChannels[channel].mNumberOfAgents == 1) { + NotifyChannelActive(aAgent->WindowID(), + static_cast(channel), + true); + } +} + +void +AudioChannelService::AudioChannelWindow::RemoveAgentAndReduceAgentsNum(AudioChannelAgent* aAgent) +{ + MOZ_ASSERT(aAgent); + MOZ_ASSERT(mAgents.Contains(aAgent)); + + int32_t channel = aAgent->AudioChannelType(); + mAgents.RemoveElement(aAgent); + + MOZ_ASSERT(mChannels[channel].mNumberOfAgents > 0); + --mChannels[channel].mNumberOfAgents; + + if (mChannels[channel].mNumberOfAgents == 0) { + NotifyChannelActive(aAgent->WindowID(), + static_cast(channel), + false); + } +} + +void +AudioChannelService::AudioChannelWindow::AudioCapturedChanged(AudioChannelAgent* aAgent, + AudioCaptureState aCapture) +{ + MOZ_ASSERT(aAgent); + + if (mIsAudioCaptured) { + aAgent->WindowAudioCaptureChanged(aAgent->InnerWindowID(), aCapture); + } +} + +void +AudioChannelService::AudioChannelWindow::AudioAudibleChanged(AudioChannelAgent* aAgent, + AudibleState aAudible) +{ + MOZ_ASSERT(aAgent); + + if (aAudible) { + AppendAudibleAgentIfNotContained(aAgent); + } else { + RemoveAudibleAgentIfContained(aAgent); + } +} + +void +AudioChannelService::AudioChannelWindow::AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent) +{ + MOZ_ASSERT(aAgent); + MOZ_ASSERT(mAgents.Contains(aAgent)); + + if (!mAudibleAgents.Contains(aAgent)) { + mAudibleAgents.AppendElement(aAgent); + if (IsFirstAudibleAgent()) { + NotifyAudioAudibleChanged(aAgent->Window(), AudibleState::eAudible); + } + } +} + +void +AudioChannelService::AudioChannelWindow::RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent) +{ + MOZ_ASSERT(aAgent); + + if (mAudibleAgents.Contains(aAgent)) { + mAudibleAgents.RemoveElement(aAgent); + if (IsLastAudibleAgent()) { + NotifyAudioAudibleChanged(aAgent->Window(), AudibleState::eNotAudible); + } + } +} + +bool +AudioChannelService::AudioChannelWindow::IsFirstAudibleAgent() const +{ + return (mAudibleAgents.Length() == 1); +} + +bool +AudioChannelService::AudioChannelWindow::IsLastAudibleAgent() const +{ + return mAudibleAgents.IsEmpty(); +} + +void +AudioChannelService::AudioChannelWindow::NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow, + AudibleState aAudible) +{ + RefPtr runnable = + new AudioPlaybackRunnable(aWindow, aAudible); + nsresult rv = NS_DispatchToCurrentThread(runnable); + NS_WARN_IF(NS_FAILED(rv)); +} + +void +AudioChannelService::AudioChannelWindow::NotifyChannelActive(uint64_t aWindowID, + AudioChannel aChannel, + bool aActive) +{ + RefPtr runnable = + new NotifyChannelActiveRunnable(aWindowID, aChannel, aActive); + nsresult rv = NS_DispatchToCurrentThread(runnable); + NS_WARN_IF(NS_FAILED(rv)); +} diff --git a/dom/audiochannel/AudioChannelService.h b/dom/audiochannel/AudioChannelService.h index 1b23162d1f..3b4fd19f02 100644 --- a/dom/audiochannel/AudioChannelService.h +++ b/dom/audiochannel/AudioChannelService.h @@ -16,6 +16,7 @@ #include "AudioChannelAgent.h" #include "nsAttrValue.h" #include "mozilla/dom/AudioChannelBinding.h" +#include "mozilla/Function.h" class nsIRunnable; class nsPIDOMWindowOuter; @@ -32,6 +33,33 @@ class TabParent; #define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_ +class AudioPlaybackConfig +{ +public: + AudioPlaybackConfig() + : mVolume(1.0) + , mMuted(false) + , mSuspend(nsISuspendedTypes::NONE_SUSPENDED) + {} + + AudioPlaybackConfig(float aVolume, bool aMuted, uint32_t aSuspended) + : mVolume(aVolume) + , mMuted(aMuted) + , mSuspend(aSuspended) + {} + + void SetConfig(float aVolume, bool aMuted, uint32_t aSuspended) + { + mVolume = aVolume; + mMuted = aMuted; + mSuspend = aSuspended; + } + + float mVolume; + bool mMuted; + uint32_t mSuspend; +}; + class AudioChannelService final : public nsIAudioChannelService , public nsIObserver { @@ -40,6 +68,16 @@ public: NS_DECL_NSIOBSERVER NS_DECL_NSIAUDIOCHANNELSERVICE + enum AudibleState : bool { + eAudible = true, + eNotAudible = false + }; + + enum AudioCaptureState : bool { + eCapturing = true, + eNotCapturing = false + }; + /** * Returns the AudioChannelServce singleton. * If AudioChannelServce is not exist, create and return new one. @@ -56,7 +94,7 @@ public: * this service, sharing the AudioChannel. */ void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, - AudioChannel aChannel); + AudibleState aAudible); /** * Any audio channel agent that stops playing should unregister itself to @@ -72,10 +110,17 @@ public: /** * Return the state to indicate this audioChannel for his window should keep - * playing/muted. + * playing/muted/suspended. */ - void GetState(nsPIDOMWindowOuter* aWindow, uint32_t aChannel, - float* aVolume, bool* aMuted); + AudioPlaybackConfig GetMediaConfig(nsPIDOMWindowOuter* aWindow, + uint32_t aAudioChannel) const; + + /** + * Called this method when the audible state of the audio playback changed, + * it would dispatch the playback event to observers which want to know the + * actual audible state of the window. + */ + void AudioAudibleChanged(AudioChannelAgent* aAgent, AudibleState aAudible); /* Methods for the BrowserElementAudioChannel */ float GetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel); @@ -114,6 +159,8 @@ public: bool AnyAudioChannelIsActive(); void RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow); + void RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow, + nsSuspendedTypes aSuspend); void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel, nsPIDOMWindowOuter* aWindow); @@ -155,6 +202,9 @@ private: AudioChannelService(); ~AudioChannelService(); + void RefreshAgents(nsPIDOMWindowOuter* aWindow, + mozilla::function aFunc); + static void CreateServiceIfNeeded(); /** @@ -170,22 +220,21 @@ private: void SetDefaultVolumeControlChannelInternal(int32_t aChannel, bool aVisible, uint64_t aChildID); - struct AudioChannelConfig final + class AudioChannelConfig final : public AudioPlaybackConfig { + public: AudioChannelConfig() - : mVolume(1.0) - , mMuted(IsAudioChannelMutedByDefault()) + : AudioPlaybackConfig(1.0, IsAudioChannelMutedByDefault(), + nsISuspendedTypes::NONE_SUSPENDED) , mNumberOfAgents(0) {} - float mVolume; - bool mMuted; - uint32_t mNumberOfAgents; }; - struct AudioChannelWindow final + class AudioChannelWindow final { + public: explicit AudioChannelWindow(uint64_t aWindowID) : mWindowID(aWindowID), mIsAudioCaptured(false) @@ -194,12 +243,36 @@ private: mChannels[(int16_t)AudioChannel::System].mMuted = false; } + void AudioAudibleChanged(AudioChannelAgent* aAgent, AudibleState aAudible); + + void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible); + void RemoveAgent(AudioChannelAgent* aAgent); + uint64_t mWindowID; bool mIsAudioCaptured; AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS]; // Raw pointer because the AudioChannelAgent must unregister itself. nsTObserverArray mAgents; + nsTObserverArray mAudibleAgents; + + private: + void AudioCapturedChanged(AudioChannelAgent* aAgent, + AudioCaptureState aCapture); + + void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent); + void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent); + + void AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent); + void RemoveAgentAndReduceAgentsNum(AudioChannelAgent* aAgent); + + bool IsFirstAudibleAgent() const; + bool IsLastAudibleAgent() const; + + void NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow, + AudibleState aAudible); + void NotifyChannelActive(uint64_t aWindowID, AudioChannel aChannel, + bool aActive); }; AudioChannelWindow* diff --git a/dom/audiochannel/nsIAudioChannelAgent.idl b/dom/audiochannel/nsIAudioChannelAgent.idl index a798cb5f64..812e389d52 100644 --- a/dom/audiochannel/nsIAudioChannelAgent.idl +++ b/dom/audiochannel/nsIAudioChannelAgent.idl @@ -6,6 +6,65 @@ interface mozIDOMWindow; +typedef uint32_t nsSuspendedTypes; + +[scriptable, builtinclass, uuid(2822a840-f009-11e5-a837-0800200c9a66)] +interface nsISuspendedTypes : nsISupports +{ + /** + * The suspended enum is used in three different situations, + * - platform audio focus (Fennec/B2G) + * - remote media control (Fennec) + * - block auto-play video in non-active page + * + * Note: the "remote side" must control the AudioChannelAgent using + * nsIAudioChannelAgentCallback.windowSuspendChanged() callback instead using + * play/pause methods or any button in the webpage. + * + * - SUSPENDED_PAUSE : + * It's used when transiently losing audio focus, the media can't be resumed + * until we gain the audio focus again. It would change the internal state of + * MediaElement when it's being suspended/resumed, and it would trigger the + * related JS event. eg. "play" and "pause" event. + * + * - SUSPENDED_BLOCK + * It's used to prevent auto-playing media in inactive page in order to + * reduce the power consumption, and the media can't be resumed until the + * page becomes active again. It would change the internal state of + * MediaElement when it's being blocked/resumed, so it won't trigger the + * related JS event. eg. "play" and "pause" event. + * + * - SUSPENDED_PAUSE_DISPOSABLE + * It's used for remote media-control to pause the playing media and when we + * lose audio focus permanently. It's disposable suspended, so the media can + * be resumed arbitrary after that. Same as SUSPENDED_PAUSE, it would change + * the internal state of MediaElement when it's being suspended. + * + * - SUSPENDED_STOP_DISPOSABLE + * It's used for remote media-control to stop the playing media. The remote + * control would disappear after stopping the media, so we would disconnect + * the audio channel agent. It's disposable suspended, so the media can be + * resumed arbitrary after that. Same as SUSPENDED_PAUSE, it would change + * the internal state of MediaElement when it's being suspended. + */ + + const uint32_t NONE_SUSPENDED = 0; + const uint32_t SUSPENDED_PAUSE = 1; + const uint32_t SUSPENDED_BLOCK = 2; + const uint32_t SUSPENDED_PAUSE_DISPOSABLE = 3; + const uint32_t SUSPENDED_STOP_DISPOSABLE = 4; +}; + +%{C++ +namespace mozilla { +namespace dom { +// It's defined in dom/audiochannel/AudioChannelService.h. +class AudioPlaybackConfig; +} +} +%} +[ptr] native AudioPlaybackConfig(mozilla::dom::AudioPlaybackConfig); + [uuid(15c05894-408e-4798-b527-a8c32d9c5f8c)] interface nsIAudioChannelAgentCallback : nsISupports { @@ -14,6 +73,11 @@ interface nsIAudioChannelAgentCallback : nsISupports */ void windowVolumeChanged(in float aVolume, in bool aMuted); + /** + * Notified when the window needs to be suspended or resumed. + */ + void windowSuspendChanged(in uint32_t aSuspend); + /** * Notified when the capture state is changed. */ @@ -25,10 +89,8 @@ interface nsIAudioChannelAgentCallback : nsISupports * in the audio channel service. Gecko components are responsible for * 1. Indicating what channel type they are using (via the init() member * function). - * 2. Before playing, checking the playable status of the channel. - * 3. Notifying the agent when they start/stop using this channel. - * 4. Notifying the agent of changes to the visibility of the component using - * this channel. + * 2. Notifying the agent when they start/stop using this channel. + * 3. Notifying the agent when they are audible. * * The agent will invoke a callback to notify Gecko components of * 1. Changes to the playable status of this channel. @@ -98,15 +160,10 @@ interface nsIAudioChannelAgent : nsISupports * Note: Gecko component SHOULD call this function first then start to * play audio stream only when return value is true. * - * @return - * normal state: the agent has registered with audio channel service and - * the component should start playback. - * muted state: the agent has registered with audio channel service but - * the component should not start playback. - * faded state: the agent has registered with audio channel service the - * component should start playback as well as reducing the volume. + * @param config + * It contains the playback related states (volume/mute/suspend) */ - void notifyStartedPlaying(out float volume, out bool muted); + void notifyStartedPlaying(in AudioPlaybackConfig config, in bool audible); /** * Notify the agent we no longer want to play. @@ -117,4 +174,14 @@ interface nsIAudioChannelAgent : nsISupports * called to unregister the agent with the channel service. */ void notifyStoppedPlaying(); + + + /** + * Notify agent that we already start producing audible data. + * + * Note : sometime audio might become silent during playing, this method is used to + * notify the actually audible state to other services which want to know + * about that, ex. tab sound indicator. + */ + void notifyStartedAudible(in bool audible); }; diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 9bf52a33cc..4a34331c3e 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -523,7 +523,7 @@ Element::WrapObject(JSContext *aCx, JS::Handle aGivenProto) } else { nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler)); + NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler)); } } } diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp index 58421cdbf7..fe13f6ae70 100644 --- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -364,11 +364,7 @@ EventSource::OnStartRequest(nsIRequest *aRequest, return NS_ERROR_ABORT; } - nsCOMPtr event = - NS_NewRunnableMethod(this, &EventSource::AnnounceConnection); - NS_ENSURE_STATE(event); - - rv = NS_DispatchToMainThread(event); + rv = NS_DispatchToMainThread(NewRunnableMethod(this, &EventSource::AnnounceConnection)); NS_ENSURE_SUCCESS(rv, rv); mStatus = PARSE_STATE_BEGIN_OF_STREAM; @@ -474,11 +470,7 @@ EventSource::OnStopRequest(nsIRequest *aRequest, ClearFields(); - nsCOMPtr event = - NS_NewRunnableMethod(this, &EventSource::ReestablishConnection); - NS_ENSURE_STATE(event); - - rv = NS_DispatchToMainThread(event); + rv = NS_DispatchToMainThread(NewRunnableMethod(this, &EventSource::ReestablishConnection)); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; @@ -900,11 +892,8 @@ EventSource::ConsoleError() nsresult EventSource::DispatchFailConnection() { - nsCOMPtr event = - NS_NewRunnableMethod(this, &EventSource::FailConnection); - NS_ENSURE_STATE(event); - return NS_DispatchToMainThread(event); + return NS_DispatchToMainThread(NewRunnableMethod(this, &EventSource::FailConnection)); } void @@ -978,7 +967,7 @@ EventSource::Thaw() nsresult rv; if (!mGoingToDispatchAllMessages && mMessagesToDispatch.GetSize() > 0) { nsCOMPtr event = - NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents); + NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents); NS_ENSURE_STATE(event); mGoingToDispatchAllMessages = true; @@ -1038,7 +1027,7 @@ EventSource::DispatchCurrentMessageEvent() if (!mGoingToDispatchAllMessages) { nsCOMPtr event = - NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents); + NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents); NS_ENSURE_STATE(event); mGoingToDispatchAllMessages = true; diff --git a/dom/base/ScreenOrientation.cpp b/dom/base/ScreenOrientation.cpp index 42990759fb..5ed7a825e0 100644 --- a/dom/base/ScreenOrientation.cpp +++ b/dom/base/ScreenOrientation.cpp @@ -540,7 +540,7 @@ ScreenOrientation::Notify(const hal::ScreenConfiguration& aConfiguration) doc->SetOrientationPendingPromise(nullptr); } - nsCOMPtr runnable = NS_NewRunnableMethod(this, + nsCOMPtr runnable = NewRunnableMethod(this, &ScreenOrientation::DispatchChangeEvent); rv = NS_DispatchToMainThread(runnable); NS_WARN_IF(NS_FAILED(rv)); @@ -615,7 +615,7 @@ ScreenOrientation::VisibleEventListener::HandleEvent(nsIDOMEvent* aEvent) doc->SetOrientationPendingPromise(nullptr); } - nsCOMPtr runnable = NS_NewRunnableMethod(orientation, + nsCOMPtr runnable = NewRunnableMethod(orientation, &ScreenOrientation::DispatchChangeEvent); rv = NS_DispatchToMainThread(runnable); if (NS_WARN_IF(rv.Failed())) { diff --git a/dom/base/WindowNamedPropertiesHandler.cpp b/dom/base/WindowNamedPropertiesHandler.cpp index 6fa3662297..a8cad79d5b 100644 --- a/dom/base/WindowNamedPropertiesHandler.cpp +++ b/dom/base/WindowNamedPropertiesHandler.cpp @@ -216,7 +216,9 @@ WindowNamedPropertiesHandler::ownPropNames(JSContext* aCx, return true; } nsHTMLDocument* document = static_cast(htmlDoc.get()); - document->GetSupportedNames(flags, names); + // Document names are enumerable, so we want to get them no matter what flags + // is. + document->GetSupportedNames(names); JS::AutoIdVector docProps(aCx); if (!AppendNamedPropertyIds(aCx, aProxy, names, false, docProps)) { diff --git a/dom/base/WindowNamedPropertiesHandler.h b/dom/base/WindowNamedPropertiesHandler.h index 628a27b608..11de900d40 100644 --- a/dom/base/WindowNamedPropertiesHandler.h +++ b/dom/base/WindowNamedPropertiesHandler.h @@ -37,9 +37,9 @@ public: delete_(JSContext* aCx, JS::Handle aProxy, JS::Handle aId, JS::ObjectOpResult &aResult) const override; - // No need for getPrototypeIfOrdinary here: this object shouldn't have a - // lazy prototype, so this trap would never be called (and the inherited - // version, from BaseProxyHandler, just crashes). + // No need for getPrototypeIfOrdinary here: window named-properties objects + // have static prototypes, so the version inherited from BaseDOMProxyHandler + // will do the right thing. virtual bool preventExtensions(JSContext* aCx, JS::Handle aProxy, diff --git a/dom/base/nsContentList.cpp b/dom/base/nsContentList.cpp index 3dea767ca8..c768fadf12 100644 --- a/dom/base/nsContentList.cpp +++ b/dom/base/nsContentList.cpp @@ -538,12 +538,8 @@ nsContentList::NamedItem(const nsAString& aName, bool aDoFlush) } void -nsContentList::GetSupportedNames(unsigned aFlags, nsTArray& aNames) +nsContentList::GetSupportedNames(nsTArray& aNames) { - if (!(aFlags & JSITER_HIDDEN)) { - return; - } - BringSelfUpToDate(true); AutoTArray atoms; diff --git a/dom/base/nsContentList.h b/dom/base/nsContentList.h index 7a34700b5d..c9b671ab82 100644 --- a/dom/base/nsContentList.h +++ b/dom/base/nsContentList.h @@ -294,8 +294,7 @@ public: aFound = !!item; return item; } - virtual void GetSupportedNames(unsigned aFlags, - nsTArray& aNames) override; + virtual void GetSupportedNames(nsTArray& aNames) override; // nsContentList public methods uint32_t Length(bool aDoFlush); diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 4ca4c90307..a74ffb2960 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -271,7 +271,7 @@ nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel) "Already dispatched an event?"); mProcessLinkHeaderEvent = - NS_NewNonOwningRunnableMethod(this, + NewNonOwningRunnableMethod(this, &nsContentSink::DoProcessLinkHeader); rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get()); if (NS_FAILED(rv)) { diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index d9c1d9a7d9..5f196a38f3 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -5048,22 +5048,29 @@ nsContentUtils::WarnScriptWasIgnored(nsIDocument* aDocument) /* static */ bool -nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) +nsContentUtils::AddScriptRunner(already_AddRefed aRunnable) { - if (!aRunnable) { + nsCOMPtr runnable = aRunnable; + if (!runnable) { return false; } if (sScriptBlockerCount) { - return sBlockedScriptRunners->AppendElement(aRunnable) != nullptr; + return sBlockedScriptRunners->AppendElement(runnable.forget()) != nullptr; } - nsCOMPtr run = aRunnable; - run->Run(); + runnable->Run(); return true; } +/* static */ +bool +nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) { + nsCOMPtr runnable = aRunnable; + return AddScriptRunner(runnable.forget()); +} + /* static */ void nsContentUtils::RunInStableState(already_AddRefed aRunnable) diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 8796a24fe7..ed0c19577b 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1609,6 +1609,7 @@ public: * has not yet been AddRefed. * @return false on out of memory, true otherwise. */ + static bool AddScriptRunner(already_AddRefed aRunnable); static bool AddScriptRunner(nsIRunnable* aRunnable); /** diff --git a/dom/base/nsDOMAttributeMap.cpp b/dom/base/nsDOMAttributeMap.cpp index 948603c7ff..fe7de268ba 100644 --- a/dom/base/nsDOMAttributeMap.cpp +++ b/dom/base/nsDOMAttributeMap.cpp @@ -163,20 +163,9 @@ nsDOMAttributeMap::NamedGetter(const nsAString& aAttrName, bool& aFound) return GetAttribute(ni); } -bool -nsDOMAttributeMap::NameIsEnumerable(const nsAString& aName) -{ - return false; -} - void -nsDOMAttributeMap::GetSupportedNames(unsigned aFlags, - nsTArray& aNames) +nsDOMAttributeMap::GetSupportedNames(nsTArray& aNames) { - if (!(aFlags & JSITER_HIDDEN)) { - return; - } - // For HTML elements in HTML documents, only include names that are still the // same after ASCII-lowercasing, since our named getter will end up // ASCII-lowercasing the given string. diff --git a/dom/base/nsDOMAttributeMap.h b/dom/base/nsDOMAttributeMap.h index 07e4111d27..31eb701e35 100644 --- a/dom/base/nsDOMAttributeMap.h +++ b/dom/base/nsDOMAttributeMap.h @@ -139,7 +139,6 @@ public: // WebIDL Attr* GetNamedItem(const nsAString& aAttrName); Attr* NamedGetter(const nsAString& aAttrName, bool& aFound); - bool NameIsEnumerable(const nsAString& aName); already_AddRefed RemoveNamedItem(mozilla::dom::NodeInfo* aNodeInfo, ErrorResult& aError); already_AddRefed @@ -159,7 +158,7 @@ public: ErrorResult& aError); void - GetSupportedNames(unsigned aFlags, nsTArray& aNames); + GetSupportedNames(nsTArray& aNames); size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index ad25b0b663..ba86e3f42f 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -1008,7 +1008,7 @@ nsDOMWindowUtils::SendNativeKeyEvent(int32_t aNativeKeyboardLayout, if (!widget) return NS_ERROR_FAILURE; - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs + NS_DispatchToMainThread(NewRunnableMethod (widget, &nsIWidget::SynthesizeNativeKeyEvent, aNativeKeyboardLayout, aNativeKeyCode, aModifiers, aCharacters, aUnmodifiedCharacters, aObserver)); @@ -1028,7 +1028,7 @@ nsDOMWindowUtils::SendNativeMouseEvent(int32_t aScreenX, if (!widget) return NS_ERROR_FAILURE; - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs + NS_DispatchToMainThread(NewRunnableMethod (widget, &nsIWidget::SynthesizeNativeMouseEvent, LayoutDeviceIntPoint(aScreenX, aScreenY), aNativeMessage, aModifierFlags, @@ -1047,7 +1047,7 @@ nsDOMWindowUtils::SendNativeMouseMove(int32_t aScreenX, if (!widget) return NS_ERROR_FAILURE; - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs + NS_DispatchToMainThread(NewRunnableMethod (widget, &nsIWidget::SynthesizeNativeMouseMove, LayoutDeviceIntPoint(aScreenX, aScreenY), aObserver)); @@ -1072,7 +1072,7 @@ nsDOMWindowUtils::SendNativeMouseScrollEvent(int32_t aScreenX, return NS_ERROR_FAILURE; } - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs + NS_DispatchToMainThread(NewRunnableMethod (widget, &nsIWidget::SynthesizeNativeMouseScrollEvent, LayoutDeviceIntPoint(aScreenX, aScreenY), aNativeMessage, aDeltaX, aDeltaY, @@ -1098,7 +1098,7 @@ nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId, return NS_ERROR_INVALID_ARG; } - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs + NS_DispatchToMainThread(NewRunnableMethod (widget, &nsIWidget::SynthesizeNativeTouchPoint, aPointerId, (nsIWidget::TouchPointerState)aTouchState, @@ -1118,7 +1118,7 @@ nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX, return NS_ERROR_FAILURE; } - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs + NS_DispatchToMainThread(NewRunnableMethod (widget, &nsIWidget::SynthesizeNativeTouchTap, LayoutDeviceIntPoint(aScreenX, aScreenY), aLongTap, aObserver)); @@ -1133,7 +1133,7 @@ nsDOMWindowUtils::ClearNativeTouchSequence(nsIObserver* aObserver) return NS_ERROR_FAILURE; } - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs + NS_DispatchToMainThread(NewRunnableMethod (widget, &nsIWidget::ClearNativeTouchSequence, aObserver)); return NS_OK; } @@ -3667,22 +3667,22 @@ nsDOMWindowUtils::PostRestyleSelfEvent(nsIDOMElement* aElement) } NS_IMETHODIMP -nsDOMWindowUtils::GetMediaSuspended(bool* aSuspended) +nsDOMWindowUtils::GetMediaSuspend(uint32_t* aSuspend) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - *aSuspended = window->GetMediaSuspended(); + *aSuspend = window->GetMediaSuspend(); return NS_OK; } NS_IMETHODIMP -nsDOMWindowUtils::SetMediaSuspended(bool aSuspended) +nsDOMWindowUtils::SetMediaSuspend(uint32_t aSuspend) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - window->SetMediaSuspended(aSuspended); + window->SetMediaSuspend(aSuspend); return NS_OK; } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index ca33abb8b8..cfbb5f667e 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -4352,7 +4352,7 @@ nsDocument::SetStyleSheetApplicableState(StyleSheetHandle aSheet, } if (!mSSApplicableStateNotificationPending) { - nsCOMPtr notification = NS_NewRunnableMethod(this, + nsCOMPtr notification = NewRunnableMethod(this, &nsDocument::NotifyStyleSheetApplicableStateChanged); mSSApplicableStateNotificationPending = NS_SUCCEEDED(NS_DispatchToCurrentThread(notification)); @@ -4943,7 +4943,7 @@ nsDocument::MaybeEndOutermostXBLUpdate() BindingManager()->EndOutermostUpdate(); } else if (!mInDestructor) { nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate)); + NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate)); } } } @@ -5261,7 +5261,7 @@ nsDocument::UnblockDOMContentLoaded() MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE); if (!mSynchronousDOMContentLoaded) { nsCOMPtr ev = - NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents); + NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents); NS_DispatchToCurrentThread(ev); } else { DispatchContentLoadedEvents(); @@ -7244,7 +7244,7 @@ nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement) return; RefPtr > event = - NS_NewNonOwningRunnableMethod(this, + NewNonOwningRunnableMethod(this, &nsDocument::DoNotifyPossibleTitleChange); nsresult rv = NS_DispatchToCurrentThread(event); if (NS_SUCCEEDED(rv)) { @@ -7393,7 +7393,7 @@ nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader) mInitializableFrameLoaders.AppendElement(aLoader); if (!mFrameLoaderRunner) { mFrameLoaderRunner = - NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); + NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); nsContentUtils::AddScriptRunner(mFrameLoaderRunner); } @@ -7411,7 +7411,7 @@ nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer) mFrameLoaderFinalizers.AppendElement(aFinalizer); if (!mFrameLoaderRunner) { mFrameLoaderRunner = - NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); + NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); nsContentUtils::AddScriptRunner(mFrameLoaderRunner); } @@ -7435,7 +7435,7 @@ nsDocument::MaybeInitializeFinalizeFrameLoaders() (mInitializableFrameLoaders.Length() || mFrameLoaderFinalizers.Length())) { mFrameLoaderRunner = - NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); + NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); nsContentUtils::AddScriptRunner(mFrameLoaderRunner); } return; @@ -9038,7 +9038,7 @@ nsDocument::BlockOnload() ++mAsyncOnloadBlockCount; if (mAsyncOnloadBlockCount == 1) { bool success = nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload)); + NewRunnableMethod(this, &nsDocument::AsyncBlockOnload)); // The script runner shouldn't fail to add. But if somebody broke // something and it does, we'll thrash at 100% cpu forever. The best @@ -12746,7 +12746,7 @@ nsDocument::GetVisibilityState() const nsDocument::PostVisibilityUpdateEvent() { nsCOMPtr event = - NS_NewRunnableMethod(this, &nsDocument::UpdateVisibilityState); + NewRunnableMethod(this, &nsDocument::UpdateVisibilityState); NS_DispatchToMainThread(event); } @@ -13483,7 +13483,7 @@ nsIDocument::RebuildUserFontSet() // change reflow). if (!mPostedFlushUserFontSet) { nsCOMPtr ev = - NS_NewRunnableMethod(this, &nsIDocument::HandleRebuildUserFontSet); + NewRunnableMethod(this, &nsIDocument::HandleRebuildUserFontSet); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { mPostedFlushUserFontSet = true; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 9df9e26a45..317b40b102 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -618,7 +618,8 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindowOuter *aOuterWindow) mMayHavePointerEnterLeaveEventListener(false), mInnerObjectsFreed(false), mIsModalContentWindow(false), - mIsActive(false), mIsBackground(false), mMediaSuspended(false), + mIsActive(false), mIsBackground(false), + mMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED), mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false), mDesktopModeViewport(false), mInnerWindow(nullptr), mOuterWindow(aOuterWindow), @@ -928,13 +929,14 @@ nsOuterWindowProxy::getPrototypeIfOrdinary(JSContext* cx, // // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof // - // We nonetheless can implement it here using a non-"lazy" [[Prototype]], - // because wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) - // supply all the non-ordinary behavior. + // We nonetheless can implement it with a static [[Prototype]], because + // wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) supply + // all non-ordinary behavior. // // But from a spec point of view, it's the exact same object in both cases -- - // only the observer's changed. So both cases *must* report non-ordinary, - // even if non-"lazy" [[Prototype]] usually means ordinary. + // only the observer's changed. So this getPrototypeIfOrdinary trap on the + // non-wrapper object *must* report non-ordinary, even if static [[Prototype]] + // usually means ordinary. *isOrdinary = false; return true; } @@ -2666,7 +2668,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, under normal circumstances, but bug 49615 describes a case.) */ nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus)); + NewRunnableMethod(this, &nsGlobalWindow::ClearStatus)); // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner // window (see bug 776497). Be safe. @@ -2983,8 +2985,8 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, // up with the outer. See bug 969156. if (createdInnerWindow) { nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(newInnerWindow, - &nsGlobalWindow::FireOnNewGlobalObject)); + NewRunnableMethod(newInnerWindow, + &nsGlobalWindow::FireOnNewGlobalObject)); } if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { @@ -2997,7 +2999,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) { newInnerWindow->mHasNotifiedGlobalCreated = true; nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated)); + NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated)); } } @@ -3889,30 +3891,29 @@ nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() } } -bool -nsPIDOMWindowOuter::GetMediaSuspended() const +SuspendTypes +nsPIDOMWindowOuter::GetMediaSuspend() const { if (IsInnerWindow()) { - return mOuterWindow->GetMediaSuspended(); + return mOuterWindow->GetMediaSuspend(); } - return mMediaSuspended; + return mMediaSuspend; } void -nsPIDOMWindowOuter::SetMediaSuspended(bool aSuspended) +nsPIDOMWindowOuter::SetMediaSuspend(SuspendTypes aSuspend) { if (IsInnerWindow()) { - mOuterWindow->SetMediaSuspended(aSuspended); + mOuterWindow->SetMediaSuspend(aSuspend); return; } - if (mMediaSuspended == aSuspended) { - return; + if (!IsDisposableSuspend(aSuspend)) { + mMediaSuspend = aSuspend; } - mMediaSuspended = aSuspended; - RefreshMediaElements(); + RefreshMediaElementsSuspend(aSuspend); } bool @@ -3938,7 +3939,7 @@ nsPIDOMWindowOuter::SetAudioMuted(bool aMuted) } mAudioMuted = aMuted; - RefreshMediaElements(); + RefreshMediaElementsVolume(); } float @@ -3967,12 +3968,12 @@ nsPIDOMWindowOuter::SetAudioVolume(float aVolume) } mAudioVolume = aVolume; - RefreshMediaElements(); + RefreshMediaElementsVolume(); return NS_OK; } void -nsPIDOMWindowOuter::RefreshMediaElements() +nsPIDOMWindowOuter::RefreshMediaElementsVolume() { RefPtr service = AudioChannelService::GetOrCreate(); if (service) { @@ -3980,6 +3981,22 @@ nsPIDOMWindowOuter::RefreshMediaElements() } } +void +nsPIDOMWindowOuter::RefreshMediaElementsSuspend(SuspendTypes aSuspend) +{ + RefPtr service = AudioChannelService::GetOrCreate(); + if (service) { + service->RefreshAgentsSuspend(GetOuterWindow(), aSuspend); + } +} + +bool +nsPIDOMWindowOuter::IsDisposableSuspend(SuspendTypes aSuspend) const +{ + return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE || + aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE); +} + void nsPIDOMWindowOuter::SetServiceWorkersTestingEnabled(bool aEnabled) { @@ -11731,7 +11748,7 @@ public: ~AutoUnblockScriptClosing() { void (nsGlobalWindow::*run)() = &nsGlobalWindow::UnblockScriptedClosing; - NS_DispatchToCurrentThread(NS_NewRunnableMethod(mWin, run)); + NS_DispatchToCurrentThread(NewRunnableMethod(mWin, run)); } }; diff --git a/dom/base/nsMimeTypeArray.cpp b/dom/base/nsMimeTypeArray.cpp index 9ddef854bd..d99b37df7a 100644 --- a/dom/base/nsMimeTypeArray.cpp +++ b/dom/base/nsMimeTypeArray.cpp @@ -90,7 +90,7 @@ nsMimeTypeArray::IndexedGetter(uint32_t aIndex, bool &aFound) } static nsMimeType* -FindMimeType(const nsTArray >& aMimeTypes, +FindMimeType(const nsTArray>& aMimeTypes, const nsAString& aType) { for (uint32_t i = 0; i < aMimeTypes.Length(); ++i) { @@ -122,12 +122,6 @@ nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound) return nullptr; } -bool -nsMimeTypeArray::NameIsEnumerable(const nsAString& aName) -{ - return true; -} - uint32_t nsMimeTypeArray::Length() { @@ -137,7 +131,7 @@ nsMimeTypeArray::Length() } void -nsMimeTypeArray::GetSupportedNames(unsigned, nsTArray< nsString >& aRetval) +nsMimeTypeArray::GetSupportedNames(nsTArray& aRetval) { EnsurePluginMimeTypes(); diff --git a/dom/base/nsMimeTypeArray.h b/dom/base/nsMimeTypeArray.h index be7b8cbcfd..3b0a882112 100644 --- a/dom/base/nsMimeTypeArray.h +++ b/dom/base/nsMimeTypeArray.h @@ -35,9 +35,8 @@ public: nsMimeType* NamedItem(const nsAString& name); nsMimeType* IndexedGetter(uint32_t index, bool &found); nsMimeType* NamedGetter(const nsAString& name, bool &found); - bool NameIsEnumerable(const nsAString& name); uint32_t Length(); - void GetSupportedNames(unsigned, nsTArray< nsString >& retval); + void GetSupportedNames(nsTArray& retval); protected: virtual ~nsMimeTypeArray(); diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index 31bf8b66fa..3a073816e2 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -39,6 +39,8 @@ class nsPIWindowRoot; class nsXBLPrototypeHandler; struct nsTimeout; +typedef uint32_t SuspendTypes; + namespace mozilla { namespace dom { class AudioContext; @@ -661,7 +663,19 @@ protected: // "active". Only used on outer windows. bool mIsBackground; - bool mMediaSuspended; + /** + * The suspended types can be "disposable" or "permanent". This varable only + * stores the value about permanent suspend. + * - disposable + * To pause all playing media in that window, but doesn't affect the media + * which starts after that. + * + * - permanent + * To pause all media in that window, and also affect the media which starts + * after that. + */ + SuspendTypes mMediaSuspend; + bool mAudioMuted; float mAudioVolume; @@ -817,7 +831,9 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID) class nsPIDOMWindowOuter : public nsPIDOMWindow { protected: - void RefreshMediaElements(); + void RefreshMediaElementsVolume(); + void RefreshMediaElementsSuspend(SuspendTypes aSuspend); + bool IsDisposableSuspend(SuspendTypes aSuspend) const; public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOWOUTER_IID) @@ -868,8 +884,8 @@ public: } // Audio API - bool GetMediaSuspended() const; - void SetMediaSuspended(bool aSuspended); + SuspendTypes GetMediaSuspend() const; + void SetMediaSuspend(SuspendTypes aSuspend); bool GetAudioMuted() const; void SetAudioMuted(bool aMuted); diff --git a/dom/base/nsPluginArray.cpp b/dom/base/nsPluginArray.cpp index 9efe9c7b07..b6630c78b3 100644 --- a/dom/base/nsPluginArray.cpp +++ b/dom/base/nsPluginArray.cpp @@ -224,12 +224,6 @@ nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound) return plugin; } -bool -nsPluginArray::NameIsEnumerable(const nsAString& aName) -{ - return true; -} - uint32_t nsPluginArray::Length() { @@ -243,7 +237,7 @@ nsPluginArray::Length() } void -nsPluginArray::GetSupportedNames(unsigned, nsTArray& aRetval) +nsPluginArray::GetSupportedNames(nsTArray& aRetval) { aRetval.Clear(); @@ -419,12 +413,6 @@ nsPluginElement::NamedGetter(const nsAString& aName, bool &aFound) return nullptr; } -bool -nsPluginElement::NameIsEnumerable(const nsAString& aName) -{ - return true; -} - uint32_t nsPluginElement::Length() { @@ -434,7 +422,7 @@ nsPluginElement::Length() } void -nsPluginElement::GetSupportedNames(unsigned, nsTArray& retval) +nsPluginElement::GetSupportedNames(nsTArray& retval) { EnsurePluginMimeTypes(); diff --git a/dom/base/nsPluginArray.h b/dom/base/nsPluginArray.h index 4a92478b0d..526bb73b67 100644 --- a/dom/base/nsPluginArray.h +++ b/dom/base/nsPluginArray.h @@ -49,9 +49,8 @@ public: void Refresh(bool aReloadDocuments); nsPluginElement* IndexedGetter(uint32_t aIndex, bool &aFound); nsPluginElement* NamedGetter(const nsAString& aName, bool &aFound); - bool NameIsEnumerable(const nsAString& aName); uint32_t Length(); - void GetSupportedNames(unsigned, nsTArray& aRetval); + void GetSupportedNames(nsTArray& aRetval); private: virtual ~nsPluginArray(); @@ -91,9 +90,8 @@ public: nsMimeType* NamedItem(const nsAString& name); nsMimeType* IndexedGetter(uint32_t index, bool &found); nsMimeType* NamedGetter(const nsAString& name, bool &found); - bool NameIsEnumerable(const nsAString& aName); uint32_t Length(); - void GetSupportedNames(unsigned, nsTArray& retval); + void GetSupportedNames(nsTArray& retval); nsTArray >& MimeTypes(); diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 2b1a3f6f04..3ce2c3c3f5 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -523,8 +523,8 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) if (!scriptURI) { // Asynchronously report the failure to create a URI object NS_DispatchToCurrentThread( - NS_NewRunnableMethod(aElement, - &nsIScriptElement::FireErrorEvent)); + NewRunnableMethod(aElement, + &nsIScriptElement::FireErrorEvent)); return false; } @@ -590,8 +590,8 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) if (NS_FAILED(rv)) { // Asynchronously report the load failure NS_DispatchToCurrentThread( - NS_NewRunnableMethod(aElement, - &nsIScriptElement::FireErrorEvent)); + NewRunnableMethod(aElement, + &nsIScriptElement::FireErrorEvent)); return false; } } @@ -1170,10 +1170,8 @@ void nsScriptLoader::ProcessPendingRequestsAsync() { if (mParserBlockingRequest || !mPendingChildLoaders.IsEmpty()) { - nsCOMPtr ev = NS_NewRunnableMethod(this, - &nsScriptLoader::ProcessPendingRequests); - - NS_DispatchToCurrentThread(ev); + NS_DispatchToCurrentThread(NewRunnableMethod(this, + &nsScriptLoader::ProcessPendingRequests)); } } diff --git a/dom/base/nsTextNode.cpp b/dom/base/nsTextNode.cpp index 2ee7b82b15..25c2d35256 100644 --- a/dom/base/nsTextNode.cpp +++ b/dom/base/nsTextNode.cpp @@ -273,8 +273,7 @@ nsAttributeTextNode::AttributeChanged(nsIDocument* aDocument, // that if we get unbound while the event is up that's ok -- we'll just // have no grandparent when it fires, and will do nothing. void (nsAttributeTextNode::*update)() = &nsAttributeTextNode::UpdateText; - nsCOMPtr ev = NS_NewRunnableMethod(this, update); - nsContentUtils::AddScriptRunner(ev); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, update)); } } diff --git a/dom/base/test/audio.ogg b/dom/base/test/audio.ogg index d7f6a0ccf47fbc30e936b47e3f4d2cf8f4a90a16..bed764fbf132f29c1004c3215730ceec956c8684 100644 GIT binary patch literal 14293 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 SK^Za4XNe^+Qciyy@V@|&Yi@%8 literal 16521 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

9eH6-?Re=p+l66 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 DFy$Y2 diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 6d9c2b5128..2f2ae4f7ff 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -10975,15 +10975,11 @@ class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod): if self.descriptor.supportsNamedProperties(): operations = self.descriptor.operations readonly = toStringBool(operations['NamedSetter'] is None) - enumerable = ( - "self->NameIsEnumerable(Constify(%s))" % - # First [0] means first (and only) signature, [1] means - # "arguments" as opposed to return type, [0] means first (and - # only) argument. - operations['NamedGetter'].signatures()[0][1][0].identifier.name) fillDescriptor = ( "FillPropertyDescriptor(desc, proxy, %s, %s);\n" - "return true;\n" % (readonly, enumerable)) + "return true;\n" % + (readonly, + toStringBool(self.descriptor.namedPropertiesEnumerable))) templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()', 'obj': 'proxy', 'successCode': fillDescriptor} @@ -11299,14 +11295,17 @@ class CGDOMJSProxyHandler_ownPropNames(ClassMethod): shadow = "false" addNames = fill( """ - nsTArray names; - UnwrapProxy(proxy)->GetSupportedNames(flags, names); + UnwrapProxy(proxy)->GetSupportedNames(names); if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) { return false; } """, shadow=shadow) + if not self.descriptor.namedPropertiesEnumerable: + addNames = CGIfWrapper(CGGeneric(addNames), + "flags & JSITER_HIDDEN").define() + addNames = "\n" + addNames else: addNames = "" @@ -11666,6 +11665,23 @@ class CGDOMJSProxyHandler_getInstance(ClassMethod): """) +class CGDOMJSProxyHandler_getPrototypeIfOrdinary(ClassMethod): + def __init__(self): + args = [Argument('JSContext*', 'cx'), + Argument('JS::Handle', 'proxy'), + Argument('bool*', 'isOrdinary'), + Argument('JS::MutableHandle', 'proto')] + + ClassMethod.__init__(self, "getPrototypeIfOrdinary", "bool", args, + virtual=True, override=True, const=True) + + def getBody(self): + return dedent(""" + *isOrdinary = false; + return true; + """) + + class CGDOMJSProxyHandler_call(ClassMethod): def __init__(self): args = [Argument('JSContext*', 'cx'), @@ -11697,7 +11713,8 @@ class CGDOMJSProxyHandler_isCallable(ClassMethod): class CGDOMJSProxyHandler(CGClass): def __init__(self, descriptor): assert (descriptor.supportsIndexedProperties() or - descriptor.supportsNamedProperties()) + descriptor.supportsNamedProperties() or + descriptor.hasNonOrdinaryGetPrototypeOf()) methods = [CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor), CGDOMJSProxyHandler_defineProperty(descriptor), ClassUsingDeclaration("mozilla::dom::DOMProxyHandler", @@ -11724,6 +11741,8 @@ class CGDOMJSProxyHandler(CGClass): (descriptor.operations['NamedSetter'] is not None and descriptor.interface.getExtendedAttribute('OverrideBuiltins'))): methods.append(CGDOMJSProxyHandler_setCustom(descriptor)) + if descriptor.hasNonOrdinaryGetPrototypeOf(): + methods.append(CGDOMJSProxyHandler_getPrototypeIfOrdinary()) if descriptor.operations['LegacyCaller']: methods.append(CGDOMJSProxyHandler_call()) methods.append(CGDOMJSProxyHandler_isCallable()) @@ -14107,7 +14126,7 @@ class CGBindingImplClass(CGClass): []), {"infallible": True})) # And if we support named properties we need to be able to - # enumerate the supported names and test whether they're enumerable. + # enumerate the supported names. if descriptor.supportsNamedProperties(): self.methodDecls.append( CGNativeMember( @@ -14115,20 +14134,7 @@ class CGBindingImplClass(CGClass): "GetSupportedNames", (IDLSequenceType(None, BuiltinTypes[IDLBuiltinType.Types.domstring]), - # Let's use unsigned long for the type here, though really - # it's just a C++ "unsigned"... - [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long], - FakeMember(), - name="aFlags")]), - {"infallible": True})) - self.methodDecls.append( - CGNativeMember( - descriptor, FakeMember(), - "NameIsEnumerable", - (BuiltinTypes[IDLBuiltinType.Types.boolean], - [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring], - FakeMember(), - name="aName")]), + []), {"infallible": True})) wrapArgs = [Argument('JSContext*', 'aCx'), diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 5845639130..b52167deef 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -515,7 +515,8 @@ class Descriptor(DescriptorProvider): self.proxy = (self.supportsIndexedProperties() or (self.supportsNamedProperties() and - not self.hasNamedPropertiesObject)) + not self.hasNamedPropertiesObject) or + self.hasNonOrdinaryGetPrototypeOf()) if self.proxy: if (not self.operations['IndexedGetter'] and @@ -747,6 +748,9 @@ class Descriptor(DescriptorProvider): def supportsNamedProperties(self): return self.operations['NamedGetter'] is not None + def hasNonOrdinaryGetPrototypeOf(self): + return self.interface.getExtendedAttribute("NonOrdinaryGetPrototypeOf") + def needsConstructHookHolder(self): assert self.interface.hasInterfaceObject() return False @@ -806,6 +810,20 @@ class Descriptor(DescriptorProvider): return (self.interface.getExtendedAttribute("Global") or self.interface.getExtendedAttribute("PrimaryGlobal")) + @property + def namedPropertiesEnumerable(self): + """ + Returns whether this interface should have enumerable named properties + """ + assert self.proxy + assert self.supportsNamedProperties() + iface = self.interface + while iface: + if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"): + return False + iface = iface.parent + return True + # Some utility methods def getTypesFromDescriptor(descriptor): diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 4d8fe57e48..4a63d45556 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -251,6 +251,16 @@ BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx, return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); } +bool +BaseDOMProxyHandler::getPrototypeIfOrdinary(JSContext* cx, JS::Handle proxy, + bool* isOrdinary, + JS::MutableHandle proto) const +{ + *isOrdinary = true; + proto.set(GetStaticPrototype(proxy)); + return true; +} + bool BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle proxy, diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index 799c3a61ae..bb740832b0 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -59,6 +59,10 @@ public: virtual bool ownPropertyKeys(JSContext* cx, JS::Handle proxy, JS::AutoIdVector &props) const override; + virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::Handle proxy, + bool* isOrdinary, + JS::MutableHandle proto) const override; + // We override getOwnEnumerablePropertyKeys() and implement it directly // instead of using the default implementation, which would call // ownPropertyKeys and then filter out the non-enumerable ones. This avoids diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index b922c7c0fd..41613d869d 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1078,6 +1078,23 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): specialMembersSeen[memberType] = member + if self.getExtendedAttribute("LegacyUnenumerableNamedProperties"): + # Check that we have a named getter. + if "named getters" not in specialMembersSeen: + raise WebIDLError( + "Interface with [LegacyUnenumerableNamedProperties] does " + "not have a named getter", + [self.location]) + ancestor = self.parent + while ancestor: + if ancestor.getExtendedAttribute("LegacyUnenumerableNamedProperties"): + raise WebIDLError( + "Interface with [LegacyUnenumerableNamedProperties] " + "inherits from another interface with " + "[LegacyUnenumerableNamedProperties]", + [self.location, ancestor.location]) + ancestor = ancestor.parent + if self._isOnGlobalProtoChain: # Make sure we have no named setters, creators, or deleters for memberType in ["setter", "creator", "deleter"]: @@ -1464,7 +1481,9 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): identifier == "Unforgeable" or identifier == "UnsafeInPrerendering" or identifier == "LegacyEventInit" or - identifier == "ProbablyShortLivingObject"): + identifier == "ProbablyShortLivingObject" or + identifier == "LegacyUnenumerableNamedProperties" or + identifier == "NonOrdinaryGetPrototypeOf"): # Known extended attributes that do not take values if not attr.noArguments(): raise WebIDLError("[%s] must take no arguments" % identifier, diff --git a/dom/bindings/parser/tests/test_unenumerable_own_properties.py b/dom/bindings/parser/tests/test_unenumerable_own_properties.py new file mode 100644 index 0000000000..d017d5ce09 --- /dev/null +++ b/dom/bindings/parser/tests/test_unenumerable_own_properties.py @@ -0,0 +1,64 @@ +def WebIDLTest(parser, harness): + + parser.parse( + """ + interface Foo {}; + [LegacyUnenumerableNamedProperties] + interface Bar : Foo { + getter long(DOMString name); + }; + interface Baz : Bar { + getter long(DOMString name); + }; + """); + results = parser.finish(); + harness.check(len(results), 3, "Should have three interfaces") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [LegacyUnenumerableNamedProperties] + interface NoNamedGetter { + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [LegacyUnenumerableNamedProperties=Foo] + interface ShouldNotHaveArg { + getter long(DOMString name); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [LegacyUnenumerableNamedProperties] + interface Foo { + getter long(DOMString name); + }; + interface Bar : Foo {}; + [LegacyUnenumerableNamedProperties] + interface Baz : Bar { + getter long(DOMString name); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index 88bba7069b..91055044db 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -1137,8 +1137,7 @@ public: virtual nsISupports* GetParentObject(); void NamedGetter(const nsAString&, bool&, nsAString&); - bool NameIsEnumerable(const nsAString&); - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); }; class TestIndexedGetterAndSetterAndNamedGetterInterface : public nsISupports, @@ -1151,8 +1150,7 @@ public: virtual nsISupports* GetParentObject(); void NamedGetter(const nsAString&, bool&, nsAString&); - bool NameIsEnumerable(const nsAString&); - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); int32_t IndexedGetter(uint32_t, bool&); void IndexedSetter(uint32_t, int32_t); uint32_t Length(); @@ -1169,10 +1167,9 @@ public: uint32_t IndexedGetter(uint32_t, bool&); void NamedGetter(const nsAString&, bool&, nsAString&); - bool NameIsEnumerable(const nsAString&); void NamedItem(const nsAString&, nsAString&); uint32_t Length(); - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); }; class TestIndexedSetterInterface : public nsISupports, @@ -1201,8 +1198,7 @@ public: void NamedSetter(const nsAString&, TestIndexedSetterInterface&); TestIndexedSetterInterface* NamedGetter(const nsAString&, bool&); - bool NameIsEnumerable(const nsAString&); - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); }; class TestIndexedAndNamedSetterInterface : public nsISupports, @@ -1219,9 +1215,8 @@ public: uint32_t Length(); void NamedSetter(const nsAString&, TestIndexedSetterInterface&); TestIndexedSetterInterface* NamedGetter(const nsAString&, bool&); - bool NameIsEnumerable(const nsAString&); void SetNamedItem(const nsAString&, TestIndexedSetterInterface&); - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); }; class TestIndexedAndNamedGetterAndSetterInterface : public TestIndexedSetterInterface @@ -1230,14 +1225,13 @@ public: uint32_t IndexedGetter(uint32_t, bool&); uint32_t Item(uint32_t); void NamedGetter(const nsAString&, bool&, nsAString&); - bool NameIsEnumerable(const nsAString&); void NamedItem(const nsAString&, nsAString&); void IndexedSetter(uint32_t, int32_t&); void IndexedSetter(uint32_t, const nsAString&) = delete; void NamedSetter(const nsAString&, const nsAString&); void Stringify(nsAString&); uint32_t Length(); - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); }; class TestCppKeywordNamedMethodsInterface : public nsISupports, @@ -1299,8 +1293,7 @@ public: void NamedDeleter(const nsAString&, bool&); long NamedGetter(const nsAString&, bool&); - bool NameIsEnumerable(const nsAString&); - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); }; class TestNamedDeleterWithRetvalInterface : public nsISupports, @@ -1315,10 +1308,9 @@ public: bool NamedDeleter(const nsAString&, bool&); bool NamedDeleter(const nsAString&) = delete; long NamedGetter(const nsAString&, bool&); - bool NameIsEnumerable(const nsAString&); bool DelNamedItem(const nsAString&); bool DelNamedItem(const nsAString&, bool&) = delete; - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); }; class TestIndexedAndNamedDeleterInterface : public nsISupports, @@ -1337,10 +1329,9 @@ public: void NamedDeleter(const nsAString&, bool&); void NamedDeleter(const nsAString&) = delete; long NamedGetter(const nsAString&, bool&); - bool NameIsEnumerable(const nsAString&); void DelNamedItem(const nsAString&); void DelNamedItem(const nsAString&, bool&) = delete; - void GetSupportedNames(unsigned, nsTArray&); + void GetSupportedNames(nsTArray&); }; class TestParentInterface : public nsISupports, diff --git a/dom/browser-element/mochitest/audio.ogg b/dom/browser-element/mochitest/audio.ogg index d7f6a0ccf47fbc30e936b47e3f4d2cf8f4a90a16..44ab64f81153cb46ec5b2db5efbf91fa1a2401b4 100644 GIT binary patch literal 14293 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

9eH6-?Re=p+l66 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 DFy$Y2 diff --git a/dom/cache/Context.cpp b/dom/cache/Context.cpp index 4c778c3aa1..a27287af82 100644 --- a/dom/cache/Context.cpp +++ b/dom/cache/Context.cpp @@ -740,7 +740,7 @@ Context::ThreadsafeHandle::AllowToClose() // Dispatch is guaranteed to succeed here because we block shutdown until // all Contexts have been destroyed. nsCOMPtr runnable = - NS_NewRunnableMethod(this, &ThreadsafeHandle::AllowToCloseOnOwningThread); + NewRunnableMethod(this, &ThreadsafeHandle::AllowToCloseOnOwningThread); MOZ_ALWAYS_SUCCEEDS( mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)); } @@ -756,7 +756,7 @@ Context::ThreadsafeHandle::InvalidateAndAllowToClose() // Dispatch is guaranteed to succeed here because we block shutdown until // all Contexts have been destroyed. nsCOMPtr runnable = - NS_NewRunnableMethod(this, &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread); + NewRunnableMethod(this, &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread); MOZ_ALWAYS_SUCCEEDS( mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)); } @@ -780,10 +780,7 @@ Context::ThreadsafeHandle::~ThreadsafeHandle() // Dispatch is guaranteed to succeed here because we block shutdown until // all Contexts have been destroyed. - nsCOMPtr runnable = - NS_NewNonOwningRunnableMethod(mStrongRef.forget().take(), &Context::Release); - MOZ_ALWAYS_SUCCEEDS( - mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)); + NS_ProxyRelease(mOwningThread, mStrongRef.forget()); } void diff --git a/dom/cache/Manager.cpp b/dom/cache/Manager.cpp index 7eff4a057f..c60ccb901e 100644 --- a/dom/cache/Manager.cpp +++ b/dom/cache/Manager.cpp @@ -912,7 +912,7 @@ private: // May be on any thread, including STS event target. Non-owning runnable // here since we are guaranteed the Action will survive until // CompleteOnInitiatingThread is called. - nsCOMPtr runnable = NS_NewNonOwningRunnableMethodWithArgs( + nsCOMPtr runnable = NewNonOwningRunnableMethod( this, &CachePutAllAction::OnAsyncCopyComplete, aRv); MOZ_ALWAYS_SUCCEEDS( mTargetThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)); @@ -1761,9 +1761,7 @@ Manager::~Manager() // Don't spin the event loop in the destructor waiting for the thread to // shutdown. Defer this to the main thread, instead. - nsCOMPtr runnable = - NS_NewRunnableMethod(ioThread, &nsIThread::Shutdown); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioThread, &nsIThread::Shutdown))); } void diff --git a/dom/camera/CameraPreviewMediaStream.cpp b/dom/camera/CameraPreviewMediaStream.cpp index 32af686eac..0233c5e518 100644 --- a/dom/camera/CameraPreviewMediaStream.cpp +++ b/dom/camera/CameraPreviewMediaStream.cpp @@ -171,9 +171,7 @@ CameraPreviewMediaStream::SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Im ++mInvalidatePending; } - nsCOMPtr event = - NS_NewRunnableMethod(this, &CameraPreviewMediaStream::Invalidate); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(this, &CameraPreviewMediaStream::Invalidate)); } void @@ -184,9 +182,7 @@ CameraPreviewMediaStream::ClearCurrentFrame() for (nsTArray >::size_type i = 0; i < mVideoOutputs.Length(); ++i) { VideoFrameContainer* output = mVideoOutputs[i]; output->ClearCurrentFrame(); - nsCOMPtr event = - NS_NewRunnableMethod(output, &VideoFrameContainer::Invalidate); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(output, &VideoFrameContainer::Invalidate)); } } diff --git a/dom/camera/DOMCameraCapabilities.cpp b/dom/camera/DOMCameraCapabilities.cpp index 54f1ee2945..4d2be2b5a3 100644 --- a/dom/camera/DOMCameraCapabilities.cpp +++ b/dom/camera/DOMCameraCapabilities.cpp @@ -239,10 +239,10 @@ CameraRecorderProfiles::~CameraRecorderProfiles() } void -CameraRecorderProfiles::GetSupportedNames(unsigned aFlags, nsTArray& aNames) +CameraRecorderProfiles::GetSupportedNames(nsTArray& aNames) { - DOM_CAMERA_LOGT("%s:%d : this=%p, flags=0x%x\n", - __func__, __LINE__, this, aFlags); + DOM_CAMERA_LOGT("%s:%d : this=%p\n", + __func__, __LINE__, this); if (!mCameraControl) { aNames.Clear(); return; @@ -275,15 +275,6 @@ CameraRecorderProfiles::NamedGetter(const nsAString& aName, bool& aFound) return profile; } -bool -CameraRecorderProfiles::NameIsEnumerable(const nsAString& aName) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p, name='%s' (always returns true)\n", - __func__, __LINE__, this, NS_ConvertUTF16toUTF8(aName).get()); - - return true; -} - void CameraRecorderProfiles::OnHardwareClosed() { diff --git a/dom/camera/DOMCameraCapabilities.h b/dom/camera/DOMCameraCapabilities.h index c50c2579cc..20fa962b1d 100644 --- a/dom/camera/DOMCameraCapabilities.h +++ b/dom/camera/DOMCameraCapabilities.h @@ -161,8 +161,7 @@ public: virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; CameraRecorderProfile* NamedGetter(const nsAString& aName, bool& aFound); - bool NameIsEnumerable(const nsAString& aName); - void GetSupportedNames(unsigned aFlags, nsTArray& aNames); + void GetSupportedNames(nsTArray& aNames); virtual void OnHardwareClosed(); diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp index f34055b222..2505bf28d0 100644 --- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -73,10 +73,8 @@ public: TrackID aInputTrackID) override { if (aTrackEvents & TRACK_EVENT_CREATED) { - nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs( - this, &TrackCreatedListener::DoNotifyTrackCreated, aID); - aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); + aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod( + this, &TrackCreatedListener::DoNotifyTrackCreated, aID)); } } diff --git a/dom/camera/DOMCameraManager.cpp b/dom/camera/DOMCameraManager.cpp index 8583494c10..68054dc5db 100644 --- a/dom/camera/DOMCameraManager.cpp +++ b/dom/camera/DOMCameraManager.cpp @@ -233,11 +233,11 @@ CameraPermissionRequest::DispatchCallback(uint32_t aPermission) { nsCOMPtr callbackRunnable; if (aPermission == nsIPermissionManager::ALLOW_ACTION) { - callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallAllow); + callbackRunnable = NewRunnableMethod(this, &CameraPermissionRequest::CallAllow); } else { - callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallCancel); + callbackRunnable = NewRunnableMethod(this, &CameraPermissionRequest::CallCancel); } - return NS_DispatchToMainThread(callbackRunnable); + return NS_DispatchToMainThread(callbackRunnable.forget()); } void diff --git a/dom/devicestorage/DeviceStorageStatics.cpp b/dom/devicestorage/DeviceStorageStatics.cpp index f02d904fdc..b8de88f5ff 100644 --- a/dom/devicestorage/DeviceStorageStatics.cpp +++ b/dom/devicestorage/DeviceStorageStatics.cpp @@ -488,7 +488,7 @@ DeviceStorageStatics::AddListener(nsDOMDeviceStorage* aListener) MOZ_ASSERT(sInstance->mInitialized); if (sInstance->mListeners.IsEmpty()) { NS_DispatchToMainThread( - NS_NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Register)); + NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Register)); } RefPtr wrapper = @@ -519,7 +519,7 @@ DeviceStorageStatics::RemoveListener(nsDOMDeviceStorage* aListener) if (removed && sInstance->mListeners.IsEmpty()) { NS_DispatchToMainThread( - NS_NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Deregister)); + NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Deregister)); } } diff --git a/dom/events/EventListenerService.cpp b/dom/events/EventListenerService.cpp index dee437b422..9b154d4ed2 100644 --- a/dom/events/EventListenerService.cpp +++ b/dom/events/EventListenerService.cpp @@ -371,9 +371,8 @@ EventListenerService::NotifyAboutMainThreadListenerChangeInternal(dom::EventTarg if (!mPendingListenerChanges) { mPendingListenerChanges = nsArrayBase::Create(); - nsCOMPtr runnable = NS_NewRunnableMethod(this, - &EventListenerService::NotifyPendingChanges); - NS_DispatchToCurrentThread(runnable); + NS_DispatchToCurrentThread(NewRunnableMethod(this, + &EventListenerService::NotifyPendingChanges)); } RefPtr changes = mPendingListenerChangesSet.Get(aTarget); diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index e036346894..4c4eb08b10 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -83,9 +83,7 @@ FetchDriver::Fetch(FetchDriverObserver* aObserver) MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(), "Synchronous fetch not supported"); - nsCOMPtr r = - NS_NewRunnableMethod(this, &FetchDriver::ContinueFetch); - return NS_DispatchToCurrentThread(r); + return NS_DispatchToCurrentThread(NewRunnableMethod(this, &FetchDriver::ContinueFetch)); } nsresult diff --git a/dom/fmradio/FMRadio.cpp b/dom/fmradio/FMRadio.cpp index 33614ba973..d2a4e30461 100644 --- a/dom/fmradio/FMRadio.cpp +++ b/dom/fmradio/FMRadio.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/PFMRadioChild.h" #include "mozilla/dom/FMRadioService.h" #include "mozilla/dom/TypedArray.h" +#include "AudioChannelService.h" #include "DOMRequest.h" #include "nsDOMClassInfo.h" #include "nsIDocShell.h" @@ -452,10 +453,15 @@ FMRadio::EnableAudioChannelAgent() { NS_ENSURE_TRUE_VOID(mAudioChannelAgent); - float volume = 0.0; - bool muted = true; - mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted); - WindowVolumeChanged(volume, muted); + AudioPlaybackConfig config; + nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config, + AudioChannelService::AudibleState::eAudible); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + WindowVolumeChanged(config.mVolume, config.mMuted); + WindowSuspendChanged(config.mSuspend); mAudioChannelAgentEnabled = true; } @@ -463,8 +469,16 @@ FMRadio::EnableAudioChannelAgent() NS_IMETHODIMP FMRadio::WindowVolumeChanged(float aVolume, bool aMuted) { + // TODO : Not support to change volume now, so we just close it. IFMRadioService::Singleton()->EnableAudio(!aMuted); - // TODO: what about the volume? + return NS_OK; +} + +NS_IMETHODIMP +FMRadio::WindowSuspendChanged(nsSuspendedTypes aSuspend) +{ + bool enable = (aSuspend == nsISuspendedTypes::NONE_SUSPENDED); + IFMRadioService::Singleton()->EnableAudio(enable); return NS_OK; } diff --git a/dom/html/HTMLAllCollection.cpp b/dom/html/HTMLAllCollection.cpp index edf9bbe658..afa160e0ce 100644 --- a/dom/html/HTMLAllCollection.cpp +++ b/dom/html/HTMLAllCollection.cpp @@ -160,12 +160,8 @@ HTMLAllCollection::NamedGetter(const nsAString& aID, } void -HTMLAllCollection::GetSupportedNames(unsigned aFlags, nsTArray& aNames) +HTMLAllCollection::GetSupportedNames(nsTArray& aNames) { - if (!(aFlags & JSITER_HIDDEN)) { - return; - } - // XXXbz this is very similar to nsContentList::GetSupportedNames, // but has to check IsAllNamedElement for the name case. AutoTArray atoms; diff --git a/dom/html/HTMLAllCollection.h b/dom/html/HTMLAllCollection.h index 61f0f170b2..2410e5ea58 100644 --- a/dom/html/HTMLAllCollection.h +++ b/dom/html/HTMLAllCollection.h @@ -62,11 +62,7 @@ public: void NamedGetter(const nsAString& aName, bool& aFound, Nullable& aResult); - void GetSupportedNames(unsigned aFlags, nsTArray& aNames); - bool NameIsEnumerable(const nsAString& aName) - { - return false; - } + void GetSupportedNames(nsTArray& aNames); void LegacyCall(JS::Handle, const nsAString& aName, Nullable& aResult) { diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 835a114f6e..d61fbd8ae3 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -223,7 +223,7 @@ HTMLCanvasPrintState::Done() mCanvas->InvalidateCanvas(); } RefPtr > doneEvent = - NS_NewRunnableMethod(this, &HTMLCanvasPrintState::NotifyDone); + NewRunnableMethod(this, &HTMLCanvasPrintState::NotifyDone); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(doneEvent))) { mPendingNotify = true; } @@ -503,7 +503,7 @@ HTMLCanvasElement::DispatchPrintCallback(nsITimerCallback* aCallback) mPrintState = new HTMLCanvasPrintState(this, mCurrentContext, aCallback); RefPtr > renderEvent = - NS_NewRunnableMethod(this, &HTMLCanvasElement::CallPrintCallback); + NewRunnableMethod(this, &HTMLCanvasElement::CallPrintCallback); return NS_DispatchToCurrentThread(renderEvent); } diff --git a/dom/html/HTMLFormControlsCollection.cpp b/dom/html/HTMLFormControlsCollection.cpp index f420a6f755..e4f0ab7d9a 100644 --- a/dom/html/HTMLFormControlsCollection.cpp +++ b/dom/html/HTMLFormControlsCollection.cpp @@ -390,13 +390,8 @@ HTMLFormControlsCollection::NamedGetter(const nsAString& aName, } void -HTMLFormControlsCollection::GetSupportedNames(unsigned aFlags, - nsTArray& aNames) +HTMLFormControlsCollection::GetSupportedNames(nsTArray& aNames) { - if (!(aFlags & JSITER_HIDDEN)) { - return; - } - FlushPendingNotifications(); // Just enumerate mNameLookupTable. This won't guarantee order, but // that's OK, because the HTML5 spec doesn't define an order for diff --git a/dom/html/HTMLFormControlsCollection.h b/dom/html/HTMLFormControlsCollection.h index 73f86036e3..1b8e1e62b3 100644 --- a/dom/html/HTMLFormControlsCollection.h +++ b/dom/html/HTMLFormControlsCollection.h @@ -53,8 +53,7 @@ public: bool dummy; NamedGetter(aName, dummy, aResult); } - virtual void GetSupportedNames(unsigned aFlags, - nsTArray& aNames) override; + virtual void GetSupportedNames(nsTArray& aNames) override; nsresult AddElementToTable(nsGenericHTMLFormElement* aChild, const nsAString& aName); diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index a275af72ea..f7791360f6 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -1540,14 +1540,8 @@ HTMLFormElement::NamedGetter(const nsAString& aName, bool &aFound) return nullptr; } -bool -HTMLFormElement::NameIsEnumerable(const nsAString& aName) -{ - return true; -} - void -HTMLFormElement::GetSupportedNames(unsigned, nsTArray& aRetval) +HTMLFormElement::GetSupportedNames(nsTArray& aRetval) { // TODO https://www.w3.org/Bugs/Public/show_bug.cgi?id=22320 } diff --git a/dom/html/HTMLFormElement.h b/dom/html/HTMLFormElement.h index 2bba4f0ce7..f98b387e5d 100644 --- a/dom/html/HTMLFormElement.h +++ b/dom/html/HTMLFormElement.h @@ -393,9 +393,7 @@ public: already_AddRefed NamedGetter(const nsAString& aName, bool &aFound); - bool NameIsEnumerable(const nsAString& aName); - - void GetSupportedNames(unsigned, nsTArray& aRetval); + void GetSupportedNames(nsTArray& aRetval); static int32_t CompareFormControlPosition(Element* aElement1, Element* aElement2, diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp index f26f0622d9..f370a182e6 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -614,7 +614,7 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, // loading. if (LoadingEnabled()) { nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage)); + NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage)); } } @@ -821,7 +821,7 @@ HTMLImageElement::CopyInnerTo(Element* aDest) if (!dest->InResponsiveMode() && dest->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) { nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(dest, &HTMLImageElement::MaybeLoadImage)); + NewRunnableMethod(dest, &HTMLImageElement::MaybeLoadImage)); } } diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index f7a9c7591a..09497ed478 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -4353,7 +4353,7 @@ HTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, ClearBrokenState(); RemoveStatesSilently(NS_EVENT_STATE_BROKEN); nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &HTMLInputElement::MaybeLoadImage)); + NewRunnableMethod(this, &HTMLInputElement::MaybeLoadImage)); } } diff --git a/dom/html/HTMLLinkElement.cpp b/dom/html/HTMLLinkElement.cpp index ab7a37d9ee..8de01a6f08 100644 --- a/dom/html/HTMLLinkElement.cpp +++ b/dom/html/HTMLLinkElement.cpp @@ -184,10 +184,10 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, } void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal; - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update)); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, update)); void (HTMLLinkElement::*updateImport)() = &HTMLLinkElement::UpdateImport; - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, updateImport)); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, updateImport)); CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded")); diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index b6b06640f9..295d8a8f1b 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -827,8 +827,8 @@ void HTMLMediaElement::QueueLoadFromSourceTask() { ChangeDelayLoadStatus(true); ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING); - RunInStableState( - NS_NewRunnableMethod(this, &HTMLMediaElement::LoadFromSourceChildren)); + RefPtr r = NewRunnableMethod(this, &HTMLMediaElement::LoadFromSourceChildren); + RunInStableState(r); } void HTMLMediaElement::QueueSelectResourceTask() @@ -838,8 +838,8 @@ void HTMLMediaElement::QueueSelectResourceTask() return; mHaveQueuedSelectResource = true; ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE); - RunInStableState( - NS_NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper)); + RefPtr r = NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper); + RunInStableState(r); } NS_IMETHODIMP HTMLMediaElement::Load() @@ -1147,16 +1147,6 @@ static bool IsAutoplayEnabled() return Preferences::GetBool("media.autoplay.enabled"); } -static bool IsScriptedAutoplayEnabled() -{ - return Preferences::GetBool("media.autoplay.allowscripted"); -} - -static bool UseAudioChannelAPI() -{ - return Preferences::GetBool("media.useAudioChannelAPI"); -} - void HTMLMediaElement::UpdatePreloadAction() { PreloadAction nextAction = PRELOAD_UNDEFINED; @@ -2231,6 +2221,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNo mAutoplayEnabled(true), mPaused(true), mMuted(0), + mAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED), mStatsShowing(false), mAllowCasting(false), mIsCasting(false), @@ -2391,21 +2382,7 @@ HTMLMediaElement::Play(ErrorResult& aRv) nsresult HTMLMediaElement::PlayInternal(bool aCallerIsChrome) { - // Prevent media element from being auto-started by a script when - // media.autoplay.enabled=false - if (!mHasUserInteraction - && !IsAutoplayEnabled() - && !IsScriptedAutoplayEnabled() - && !EventStateManager::IsHandlingUserInput() - && !aCallerIsChrome) { - LOG(LogLevel::Debug, ("%p Blocked attempt to autoplay media.", this)); -#if defined(MOZ_WIDGET_ANDROID) - nsContentUtils::DispatchTrustedEvent(OwnerDoc(), - static_cast(this), - NS_LITERAL_STRING("MozAutoplayMediaBlocked"), - false, - false); -#endif + if (!IsAllowedToPlay()) { return NS_OK; } @@ -2470,6 +2447,8 @@ HTMLMediaElement::PlayInternal(bool aCallerIsChrome) mPaused = false; mAutoplaying = false; + SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED); + // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference // and our preload status. AddRemoveSelfReference(); @@ -2786,7 +2765,7 @@ nsresult HTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParen if (mDecoder) { // When the MediaElement is binding to tree, the dormant status is // aligned to document's hidden status. - mDecoder->NotifyOwnerActivityChanged(); + mDecoder->NotifyOwnerActivityChanged(!IsHidden()); } return rv; @@ -2875,7 +2854,7 @@ void HTMLMediaElement::UnbindFromTree(bool aDeep, if (mDecoder) { MOZ_ASSERT(IsHidden()); - mDecoder->NotifyOwnerActivityChanged(); + mDecoder->NotifyOwnerActivityChanged(false); } } @@ -3163,9 +3142,9 @@ public: { nsCOMPtr event; if (aBlocked == BLOCKED) { - event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyBlocked); + event = NewRunnableMethod(this, &StreamListener::DoNotifyBlocked); } else { - event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked); + event = NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked); } aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); } @@ -3174,7 +3153,7 @@ public: { if (event == EVENT_FINISHED) { nsCOMPtr event = - NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished); + NewRunnableMethod(this, &StreamListener::DoNotifyFinished); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); } } @@ -3182,7 +3161,7 @@ public: { MutexAutoLock lock(mMutex); nsCOMPtr event = - NS_NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData); + NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); } virtual void NotifyOutput(MediaStreamGraph* aGraph, @@ -3193,7 +3172,7 @@ public: return; mPendingNotifyOutput = true; nsCOMPtr event = - NS_NewRunnableMethod(this, &StreamListener::DoNotifyOutput); + NewRunnableMethod(this, &StreamListener::DoNotifyOutput); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); } @@ -3243,11 +3222,13 @@ public: const VideoSegment& video = static_cast(aQueuedMedia); for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) { if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) { + mInitialSizeFound = true; nsCOMPtr event = - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( this, &StreamSizeListener::ReceivedSize, c->mFrame.GetIntrinsicSize()); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); + return; } } } @@ -3364,6 +3345,10 @@ void HTMLMediaElement::UpdateSrcMediaStreamPlaying(uint32_t aFlags) mMediaStreamListener->Forget(); mMediaStreamListener = nullptr; } + + // If the input is a media stream, we don't check its data and always regard + // it as audible when it's playing. + NotifyAudibleStateChanged(shouldPlay); } void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) @@ -4471,12 +4456,7 @@ bool HTMLMediaElement::IsBeingDestroyed() void HTMLMediaElement::NotifyOwnerDocumentActivityChanged() { bool pauseElement = NotifyOwnerDocumentActivityChangedInternal(); - if (pauseElement && mAudioChannelAgent && - // On B2G, NotifyOwnerDocumentActivityChangedInternal may return true for - // two reasons: the document no longer being active, or the element being - // paused by the audio channel. However we are only interested in the - // first case here, so we need to filter out the second case. - (!UseAudioChannelAPI() || !ComputedMuted())) { + if (pauseElement && mAudioChannelAgent) { // If the element is being paused since we are navigating away from the // document, notify the audio channel agent. // Be careful to ignore this event during a docshell frame swap. @@ -4493,21 +4473,11 @@ void HTMLMediaElement::NotifyOwnerDocumentActivityChanged() bool HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal() { - nsIDocument* ownerDoc = OwnerDoc(); if (mDecoder && !IsBeingDestroyed()) { - mDecoder->SetElementVisibility(!ownerDoc->Hidden()); - mDecoder->NotifyOwnerActivityChanged(); + mDecoder->NotifyOwnerActivityChanged(!IsHidden()); } bool pauseElement = !IsActive(); - // Only pause the element when we start playing. If we pause without playing - // audio, the resource loading would be affected unexpectedly. For example, - // the media element is muted by default, but we don't want this behavior - // interrupting the loading process. - if (UseAudioChannelAPI() && mAudioChannelAgent) { - pauseElement |= ComputedMuted(); - } - SuspendOrResumeElement(pauseElement, !IsActive()); if (!mPausedForInactiveDocumentOrChannel && @@ -4555,7 +4525,7 @@ void HTMLMediaElement::AddRemoveSelfReference() // Dispatch Release asynchronously so that we don't destroy this object // inside a call stack of method calls on this object nsCOMPtr event = - NS_NewRunnableMethod(this, &HTMLMediaElement::DoRemoveSelfReference); + NewRunnableMethod(this, &HTMLMediaElement::DoRemoveSelfReference); NS_DispatchToMainThread(event); } } @@ -4921,33 +4891,6 @@ ImageContainer* HTMLMediaElement::GetImageContainer() return container ? container->GetImageContainer() : nullptr; } -nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted) -{ - if (mAudioChannelVolume != aVolume) { - mAudioChannelVolume = aVolume; - SetVolumeInternal(); - } - - // We have to mute this channel. - if (aMuted && !ComputedMuted()) { - SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL); - if (UseAudioChannelAPI()) { - DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin")); - } - } else if (!aMuted && ComputedMuted()) { - SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL); - if (UseAudioChannelAPI()) { - DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend")); - } - } - - if (UseAudioChannelAPI()) { - SuspendOrResumeElement(ComputedMuted(), false); - } - - return NS_OK; -} - bool HTMLMediaElement::MaybeCreateAudioChannelAgent() { @@ -4968,6 +4911,11 @@ HTMLMediaElement::MaybeCreateAudioChannelAgent() bool HTMLMediaElement::IsPlayingThroughTheAudioChannel() const { + // It might be resumed from remote, we should keep the audio channel agent. + if (IsSuspendedByAudioChannel()) { + return true; + } + // Are we paused or muted if (mPaused || Muted()) { return false; @@ -5044,29 +4992,197 @@ HTMLMediaElement::NotifyAudioChannelAgent(bool aPlaying) AutoNoJSAPI nojsapi; if (aPlaying) { - float volume = 0.0; - bool muted = true; - mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted); - WindowVolumeChanged(volume, muted); + // The reason we don't call NotifyStartedPlaying after the media element + // really becomes audible is because there is another case needs to block + // element as early as we can, we would hear sound leaking if we block it + // too late. In that case (block autoplay in non-visited-tab), we need to + // create a connection before decoding, because we don't want user hearing + // any sound. + AudioPlaybackConfig config; + nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config, + mIsAudioTrackAudible); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + WindowVolumeChanged(config.mVolume, config.mMuted); + WindowSuspendChanged(config.mSuspend); } else { mAudioChannelAgent->NotifyStoppedPlaying(); mAudioChannelAgent = nullptr; } } -NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted) +NS_IMETHODIMP +HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted) { - MOZ_ASSERT(NS_IsMainThread()); + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("HTMLMediaElement, WindowVolumeChanged, this = %p, " + "aVolume = %f, aMuted = %d\n", this, aVolume, aMuted)); - UpdateChannelMuteState(aVolume, aMuted); + if (mAudioChannelVolume != aVolume) { + mAudioChannelVolume = aVolume; + SetVolumeInternal(); + } - if (UseAudioChannelAPI()) { - mPaused.SetCanPlay(!aMuted); + if (aMuted && !ComputedMuted()) { + SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL); + } else if (!aMuted && ComputedMuted()) { + SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL); } return NS_OK; } +NS_IMETHODIMP +HTMLMediaElement::WindowSuspendChanged(SuspendTypes aSuspend) +{ + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("HTMLMediaElement, WindowSuspendChanged, this = %p, " + "aSuspend = %d\n", this, aSuspend)); + + switch (aSuspend) { + case nsISuspendedTypes::NONE_SUSPENDED: + ResumeFromAudioChannel(); + break; + case nsISuspendedTypes::SUSPENDED_PAUSE: + case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE: + PauseByAudioChannel(aSuspend); + break; + case nsISuspendedTypes::SUSPENDED_BLOCK: + BlockByAudioChannel(); + break; + case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE: + Pause(); + break; + default: + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("HTMLMediaElement, WindowSuspendChanged, this = %p, " + "Error : unknown suspended type!\n", this)); + } + + return NS_OK; +} + +void +HTMLMediaElement::ResumeFromAudioChannel() +{ + if (!IsSuspendedByAudioChannel()) { + return; + } + + switch (mAudioChannelSuspended) { + case nsISuspendedTypes::SUSPENDED_PAUSE: + case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE: + ResumeFromAudioChannelPaused(mAudioChannelSuspended); + break; + case nsISuspendedTypes::SUSPENDED_BLOCK: + ResumeFromAudioChannelBlocked(); + break; + default: + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("HTMLMediaElement, ResumeFromAudioChannel, this = %p, " + "Error : resume without suspended!\n", this)); + } +} + +void +HTMLMediaElement::ResumeFromAudioChannelPaused(SuspendTypes aSuspend) +{ + MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE || + mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE); + + SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED); + nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend")); +} + +void +HTMLMediaElement::ResumeFromAudioChannelBlocked() +{ + MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK); + + SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED); + mPaused.SetCanPlay(true); + SuspendOrResumeElement(false /* resume */, false); +} + +void +HTMLMediaElement::PauseByAudioChannel(SuspendTypes aSuspend) +{ + if (IsSuspendedByAudioChannel()) { + return; + } + + SetAudioChannelSuspended(aSuspend); + Pause(); + DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin")); +} + +void +HTMLMediaElement::BlockByAudioChannel() +{ + if (IsSuspendedByAudioChannel()) { + return; + } + + SetAudioChannelSuspended(nsISuspendedTypes::SUSPENDED_BLOCK); + SuspendOrResumeElement(true /* suspend */, false); + mPaused.SetCanPlay(false); +} + +void +HTMLMediaElement::SetAudioChannelSuspended(SuspendTypes aSuspend) +{ + if (mAudioChannelSuspended == aSuspend) { + return; + } + + mAudioChannelSuspended = aSuspend; + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("HTMLMediaElement, SetAudioChannelSuspended, this = %p, " + "aSuspend = %d\n", this, aSuspend)); +} + +bool +HTMLMediaElement::IsSuspendedByAudioChannel() const +{ + return (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE || + mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE || + mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK); +} + +bool +HTMLMediaElement::IsAllowedToPlay() +{ + // Prevent media element from being auto-started by a script when + // media.autoplay.enabled=false + if (!mHasUserInteraction && + !IsAutoplayEnabled() && + !EventStateManager::IsHandlingUserInput() && + !nsContentUtils::IsCallerChrome()) { +#if defined(MOZ_WIDGET_ANDROID) + nsContentUtils::DispatchTrustedEvent(OwnerDoc(), + static_cast(this), + NS_LITERAL_STRING("MozAutoplayMediaBlocked"), + false, + false); +#endif + return false; + } + + // The MediaElement can't start playback until it's resumed by audio channel. + if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE || + mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) { + return false; + } + + return true; +} + #ifdef MOZ_EME MediaKeys* HTMLMediaElement::GetMediaKeys() const @@ -5405,6 +5521,12 @@ HTMLMediaElement::ComputedMuted() const return (mMuted & MUTED_BY_AUDIO_CHANNEL); } +nsSuspendedTypes +HTMLMediaElement::ComputedSuspended() const +{ + return mAudioChannelSuspended; +} + bool HTMLMediaElement::IsCurrentlyPlaying() const { @@ -5431,7 +5553,15 @@ HTMLMediaElement::NotifyAudibleStateChanged(bool aAudible) { if (mIsAudioTrackAudible != aAudible) { mIsAudioTrackAudible = aAudible; - // To do ... + NotifyAudioPlaybackChanged(); + } +} + +void +HTMLMediaElement::NotifyAudioPlaybackChanged() +{ + if (mAudioChannelAgent) { + mAudioChannelAgent->NotifyStartedAudible(mIsAudioTrackAudible); } } diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index c114270e6e..d7ffed5e03 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -35,6 +35,7 @@ typedef uint16_t nsMediaNetworkState; typedef uint16_t nsMediaReadyState; +typedef uint32_t SuspendTypes; namespace mozilla { class DecoderDoctorDiagnostics; @@ -448,9 +449,13 @@ public: // when the connection between Rtsp server and client gets lost. virtual void ResetConnectionState() final override; - // Called by media decoder when the audible state changed. + // Called by media decoder when the audible state changed or when input is + // a media stream. virtual void NotifyAudibleStateChanged(bool aAudible) final override; + // Notify agent when the MediaElement changes its audible state. + void NotifyAudioPlaybackChanged(); + // XPCOM GetPreload() is OK void SetPreload(const nsAString& aValue, ErrorResult& aRv) { @@ -719,9 +724,10 @@ public: IMPL_EVENT_HANDLER(mozinterruptbegin) IMPL_EVENT_HANDLER(mozinterruptend) - // This is for testing only + // These are used for testing only float ComputedVolume() const; bool ComputedMuted() const; + nsSuspendedTypes ComputedSuspended() const; protected: virtual ~HTMLMediaElement(); @@ -1112,9 +1118,6 @@ protected: // Check the permissions for audiochannel. bool CheckAudioChannelPermissions(const nsAString& aType); - // This method does the check for muting/nmuting the audio channel. - nsresult UpdateChannelMuteState(float aVolume, bool aMuted); - // Seeks to aTime seconds. aSeekType can be Exact to seek to exactly the // seek target, or PrevSyncPoint if a quicker but less precise seek is // desired, and we'll seek to the sync point (keyframe and/or start of the @@ -1149,6 +1152,38 @@ protected: // channel agent is ready to be used. bool MaybeCreateAudioChannelAgent(); + /** + * We have different kinds of suspended cases, + * - SUSPENDED_PAUSE + * It's used when we temporary lost platform audio focus. MediaElement can + * only be resumed when we gain the audio focus again. + * + * - SUSPENDED_PAUSE_DISPOSABLE + * It's used when user press the pause botton on the remote media-control. + * MediaElement can be resumed by reomte media-control or via play(). + * + * - SUSPENDED_BLOCK + * It's used to reduce the power comsuption, we won't play the auto-play + * audio/video in the page we have never visited before. MediaElement would + * be resumed when the page is active. See bug647429 for more details. + * + * - SUSPENDED_STOP_DISPOSABLE + * When we permanently lost platform audio focus, we shuold stop playing + * and stop the audio channel agent. MediaElement can only be restarted by + * play(). + */ + void PauseByAudioChannel(SuspendTypes aSuspend); + void BlockByAudioChannel(); + + void ResumeFromAudioChannel(); + void ResumeFromAudioChannelPaused(SuspendTypes aSuspend); + void ResumeFromAudioChannelBlocked(); + + bool IsSuspendedByAudioChannel() const; + void SetAudioChannelSuspended(SuspendTypes aSuspend); + + bool IsAllowedToPlay(); + class nsAsyncEventRunner; using nsGenericHTMLElement::DispatchEvent; // For nsAsyncEventRunner. @@ -1363,6 +1398,7 @@ protected: }; uint32_t mMuted; + SuspendTypes mAudioChannelSuspended; // True if the media statistics are currently being shown by the builtin // video controls diff --git a/dom/html/HTMLObjectElement.cpp b/dom/html/HTMLObjectElement.cpp index 543c5dc093..1f2fda346d 100644 --- a/dom/html/HTMLObjectElement.cpp +++ b/dom/html/HTMLObjectElement.cpp @@ -280,7 +280,7 @@ HTMLObjectElement::BindToTree(nsIDocument *aDocument, // If we already have all the children, start the load. if (mIsDoneAddingChildren && !pluginDoc) { void (HTMLObjectElement::*start)() = &HTMLObjectElement::StartObjectLoad; - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start)); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, start)); } return NS_OK; diff --git a/dom/html/HTMLOptionsCollection.cpp b/dom/html/HTMLOptionsCollection.cpp index 1435706179..1fce684aed 100644 --- a/dom/html/HTMLOptionsCollection.cpp +++ b/dom/html/HTMLOptionsCollection.cpp @@ -280,13 +280,8 @@ HTMLOptionsCollection::NamedItem(const nsAString& aName, } void -HTMLOptionsCollection::GetSupportedNames(unsigned aFlags, - nsTArray& aNames) +HTMLOptionsCollection::GetSupportedNames(nsTArray& aNames) { - if (!(aFlags & JSITER_HIDDEN)) { - return; - } - AutoTArray atoms; for (uint32_t i = 0; i < mElements.Length(); ++i) { HTMLOptionElement* content = mElements.ElementAt(i); diff --git a/dom/html/HTMLOptionsCollection.h b/dom/html/HTMLOptionsCollection.h index 3bdd1e3d61..21123b3d21 100644 --- a/dom/html/HTMLOptionsCollection.h +++ b/dom/html/HTMLOptionsCollection.h @@ -154,8 +154,7 @@ public: { aError = SetOption(aIndex, aOption); } - virtual void GetSupportedNames(unsigned aFlags, - nsTArray& aNames) override; + virtual void GetSupportedNames(nsTArray& aNames) override; private: /** The list of options (holds strong references). This is infallible, so diff --git a/dom/html/HTMLPropertiesCollection.cpp b/dom/html/HTMLPropertiesCollection.cpp index 3eb340a9d0..7ed09376d5 100644 --- a/dom/html/HTMLPropertiesCollection.cpp +++ b/dom/html/HTMLPropertiesCollection.cpp @@ -288,7 +288,7 @@ HTMLPropertiesCollection::CrawlSubtree(Element* aElement) } void -HTMLPropertiesCollection::GetSupportedNames(unsigned, nsTArray& aNames) +HTMLPropertiesCollection::GetSupportedNames(nsTArray& aNames) { EnsureFresh(); mNames->CopyList(aNames); diff --git a/dom/html/HTMLPropertiesCollection.h b/dom/html/HTMLPropertiesCollection.h index 3f5f8ef9f1..d6f086180d 100644 --- a/dom/html/HTMLPropertiesCollection.h +++ b/dom/html/HTMLPropertiesCollection.h @@ -90,17 +90,12 @@ public: aFound = IsSupportedNamedProperty(aName); return aFound ? NamedItem(aName) : nullptr; } - bool NameIsEnumerable(const nsAString& aName) - { - return true; - } DOMStringList* Names() { EnsureFresh(); return mNames; } - virtual void GetSupportedNames(unsigned, - nsTArray& aNames) override; + virtual void GetSupportedNames(nsTArray& aNames) override; NS_DECL_NSIDOMHTMLCOLLECTION diff --git a/dom/html/HTMLSharedObjectElement.cpp b/dom/html/HTMLSharedObjectElement.cpp index 9d28b48165..6d591d1894 100644 --- a/dom/html/HTMLSharedObjectElement.cpp +++ b/dom/html/HTMLSharedObjectElement.cpp @@ -153,7 +153,7 @@ HTMLSharedObjectElement::BindToTree(nsIDocument *aDocument, if (mIsDoneAddingChildren && !pluginDoc) { void (HTMLSharedObjectElement::*start)() = &HTMLSharedObjectElement::StartObjectLoad; - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start)); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, start)); } return NS_OK; diff --git a/dom/html/HTMLStyleElement.cpp b/dom/html/HTMLStyleElement.cpp index ec690fc0b4..ba44c9600c 100644 --- a/dom/html/HTMLStyleElement.cpp +++ b/dom/html/HTMLStyleElement.cpp @@ -147,7 +147,7 @@ HTMLStyleElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, NS_ENSURE_SUCCESS(rv, rv); void (HTMLStyleElement::*update)() = &HTMLStyleElement::UpdateStyleSheetInternal; - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update)); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, update)); return rv; } diff --git a/dom/html/HTMLTableElement.cpp b/dom/html/HTMLTableElement.cpp index f3f52bffdb..4dc6b68017 100644 --- a/dom/html/HTMLTableElement.cpp +++ b/dom/html/HTMLTableElement.cpp @@ -41,8 +41,7 @@ public: virtual Element* GetFirstNamedElement(const nsAString& aName, bool& aFound) override; - virtual void GetSupportedNames(unsigned aFlags, - nsTArray& aNames) override; + virtual void GetSupportedNames(nsTArray& aNames) override; NS_IMETHOD ParentDestroyed(); @@ -236,18 +235,13 @@ TableRowsCollection::GetFirstNamedElement(const nsAString& aName, bool& aFound) } void -TableRowsCollection::GetSupportedNames(unsigned aFlags, - nsTArray& aNames) +TableRowsCollection::GetSupportedNames(nsTArray& aNames) { - if (!(aFlags & JSITER_HIDDEN)) { - return; - } - DO_FOR_EACH_ROWGROUP( nsTArray names; nsCOMPtr coll = do_QueryInterface(rows); if (coll) { - coll->GetSupportedNames(aFlags, names); + coll->GetSupportedNames(names); for (uint32_t i = 0; i < names.Length(); ++i) { if (!aNames.Contains(names[i])) { aNames.AppendElement(names[i]); diff --git a/dom/html/HTMLTrackElement.cpp b/dom/html/HTMLTrackElement.cpp index 5a660b1d35..7f2982688a 100644 --- a/dom/html/HTMLTrackElement.cpp +++ b/dom/html/HTMLTrackElement.cpp @@ -260,8 +260,8 @@ HTMLTrackElement::BindToTree(nsIDocument* aDocument, media->NotifyAddedSource(); LOG(LogLevel::Debug, ("Track element sent notification to parent.")); - mMediaParent->RunInStableState( - NS_NewRunnableMethod(this, &HTMLTrackElement::LoadResource)); + RefPtr r = NewRunnableMethod(this, &HTMLTrackElement::LoadResource); + mMediaParent->RunInStableState(r); } return NS_OK; @@ -314,7 +314,7 @@ void HTMLTrackElement::DispatchTrackRunnable(const nsString& aEventName) { nsCOMPtr runnable = - NS_NewRunnableMethodWithArg + NewRunnableMethod (this, &HTMLTrackElement::DispatchTrustedEvent, aEventName); diff --git a/dom/html/ImageDocument.cpp b/dom/html/ImageDocument.cpp index dc36268c83..e1927757a9 100644 --- a/dom/html/ImageDocument.cpp +++ b/dom/html/ImageDocument.cpp @@ -473,7 +473,7 @@ ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aDa // come during painting and this will trigger invalidation. if (aType == imgINotificationObserver::HAS_TRANSPARENCY) { nsCOMPtr runnable = - NS_NewRunnableMethod(this, &ImageDocument::OnHasTransparency); + NewRunnableMethod(this, &ImageDocument::OnHasTransparency); nsContentUtils::AddScriptRunner(runnable); } @@ -534,7 +534,7 @@ ImageDocument::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) aImage->GetHeight(&mImageHeight); nsCOMPtr runnable = - NS_NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing); + NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing); nsContentUtils::AddScriptRunner(runnable); UpdateTitleAndCharset(); diff --git a/dom/html/nsDOMStringMap.cpp b/dom/html/nsDOMStringMap.cpp index a476ab1e86..e9cac5e38c 100644 --- a/dom/html/nsDOMStringMap.cpp +++ b/dom/html/nsDOMStringMap.cpp @@ -89,12 +89,6 @@ nsDOMStringMap::NamedGetter(const nsAString& aProp, bool& found, found = mElement->GetAttr(attr, aResult); } -bool -nsDOMStringMap::NameIsEnumerable(const nsAString& aName) -{ - return true; -} - void nsDOMStringMap::NamedSetter(const nsAString& aProp, const nsAString& aValue, @@ -149,7 +143,7 @@ nsDOMStringMap::NamedDeleter(const nsAString& aProp, bool& found) } void -nsDOMStringMap::GetSupportedNames(unsigned, nsTArray& aNames) +nsDOMStringMap::GetSupportedNames(nsTArray& aNames) { uint32_t attrCount = mElement->GetAttrCount(); diff --git a/dom/html/nsDOMStringMap.h b/dom/html/nsDOMStringMap.h index e38e71351a..ffb6ab5257 100644 --- a/dom/html/nsDOMStringMap.h +++ b/dom/html/nsDOMStringMap.h @@ -42,8 +42,7 @@ public: void NamedSetter(const nsAString& aProp, const nsAString& aValue, mozilla::ErrorResult& rv); void NamedDeleter(const nsAString& aProp, bool &found); - bool NameIsEnumerable(const nsAString& aName); - void GetSupportedNames(unsigned, nsTArray& aNames); + void GetSupportedNames(nsTArray& aNames); js::ExpandoAndGeneration mExpandoAndGeneration; diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index 8763af5280..835d3ce2e7 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -2342,14 +2342,8 @@ nsHTMLDocument::NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound, aRetval.set(&val.toObject()); } -bool -nsHTMLDocument::NameIsEnumerable(const nsAString& aName) -{ - return true; -} - void -nsHTMLDocument::GetSupportedNames(unsigned, nsTArray& aNames) +nsHTMLDocument::GetSupportedNames(nsTArray& aNames) { for (auto iter = mIdentifierMap.Iter(); !iter.Done(); iter.Next()) { nsIdentifierMapEntry* entry = iter.Get(); @@ -2522,7 +2516,7 @@ nsHTMLDocument::MaybeEditingStateChanged() EditingStateChanged(); } else if (!mInDestructor) { nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsHTMLDocument::MaybeEditingStateChanged)); + NewRunnableMethod(this, &nsHTMLDocument::MaybeEditingStateChanged)); } } } diff --git a/dom/html/nsHTMLDocument.h b/dom/html/nsHTMLDocument.h index a18315e82b..857637788f 100644 --- a/dom/html/nsHTMLDocument.h +++ b/dom/html/nsHTMLDocument.h @@ -174,8 +174,7 @@ public: void NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound, JS::MutableHandle aRetval, mozilla::ErrorResult& rv); - bool NameIsEnumerable(const nsAString& aName); - void GetSupportedNames(unsigned, nsTArray& aNames); + void GetSupportedNames(nsTArray& aNames); nsGenericHTMLElement *GetBody(); void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv); mozilla::dom::HTMLSharedElement *GetHead() { diff --git a/dom/html/nsIHTMLCollection.h b/dom/html/nsIHTMLCollection.h index 11925ab177..7dbfe87665 100644 --- a/dom/html/nsIHTMLCollection.h +++ b/dom/html/nsIHTMLCollection.h @@ -69,15 +69,10 @@ public: { return GetFirstNamedElement(aName, aFound); } - bool NameIsEnumerable(const nsAString& aName) - { - return false; - } virtual mozilla::dom::Element* GetFirstNamedElement(const nsAString& aName, bool& aFound) = 0; - virtual void GetSupportedNames(unsigned aFlags, - nsTArray& aNames) = 0; + virtual void GetSupportedNames(nsTArray& aNames) = 0; JSObject* GetWrapperPreserveColor() { diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index d320e566d5..cd7eb2866d 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -11831,7 +11831,7 @@ ConnectionPool::ShutdownThread(ThreadInfo& aThreadInfo) MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(runnable, NS_DISPATCH_NORMAL)); nsCOMPtr shutdownRunnable = - NS_NewRunnableMethod(thread, &nsIThread::Shutdown); + NewRunnableMethod(thread, &nsIThread::Shutdown); MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(shutdownRunnable)); mTotalThreadCount--; @@ -13681,7 +13681,7 @@ Database::MaybeCloseConnection() IsClosed() && mDirectoryLock) { nsCOMPtr callback = - NS_NewRunnableMethod(this, &Database::ConnectionClosedCallback); + NewRunnableMethod(this, &Database::ConnectionClosedCallback); RefPtr helper = new WaitForTransactionsHelper(Id(), callback); @@ -21427,7 +21427,7 @@ OpenDatabaseOp::SendResults() mDatabase = nullptr; } else if (mDirectoryLock) { nsCOMPtr callback = - NS_NewRunnableMethod(this, &OpenDatabaseOp::ConnectionClosedCallback); + NewRunnableMethod(this, &OpenDatabaseOp::ConnectionClosedCallback); RefPtr helper = new WaitForTransactionsHelper(mDatabaseId, callback); diff --git a/dom/indexedDB/FileSnapshot.cpp b/dom/indexedDB/FileSnapshot.cpp index 3faecdd62e..f4f0d6dd57 100644 --- a/dom/indexedDB/FileSnapshot.cpp +++ b/dom/indexedDB/FileSnapshot.cpp @@ -87,7 +87,7 @@ private: } nsCOMPtr destroyRunnable = - NS_NewNonOwningRunnableMethod(this, &StreamWrapper::Destroy); + NewNonOwningRunnableMethod(this, &StreamWrapper::Destroy); MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(destroyRunnable, NS_DISPATCH_NORMAL)); diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index d056486347..2f934c9d4e 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -1036,9 +1036,9 @@ IDBDatabase::DelayedMaybeExpireFileActors() } nsCOMPtr runnable = - NS_NewRunnableMethodWithArg(this, - &IDBDatabase::ExpireFileActors, - /* aExpireAll */ false); + NewRunnableMethod(this, + &IDBDatabase::ExpireFileActors, + /* aExpireAll */ false); MOZ_ASSERT(runnable); if (!NS_IsMainThread()) { diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 432481ffba..99487a28da 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -1751,10 +1751,10 @@ interface nsIDOMWindowUtils : nsISupports { void postRestyleSelfEvent(in nsIDOMElement aElement); /** - * Used to pause or resume all MediaElements in this window. Use-cases are - * audio competing and remote media control. + * Used to pause or resume all media in this window. Use-cases are audio + * competing, remote media control and to prevent auto-playing media. */ - attribute boolean mediaSuspended; + attribute uint32_t mediaSuspend; /** * With this it's possible to mute all the MediaElements in this window. diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index adddee64e2..263324bf87 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -293,7 +293,7 @@ ReleaseOnTarget(SmartPtr& aDoomed, nsIEventTarget* aTarget) auto* doomedSupports = static_cast(doomedRaw); nsCOMPtr releaseRunnable = - NS_NewNonOwningRunnableMethod(doomedSupports, &nsISupports::Release); + NewNonOwningRunnableMethod(doomedSupports, &nsISupports::Release); MOZ_ASSERT(releaseRunnable); if (aTarget) { @@ -1600,11 +1600,7 @@ private: NS_WARN_IF_FALSE(NS_SUCCEEDED(stream->Close()), "Failed to close stream!"); - nsCOMPtr shutdownRunnable = - NS_NewRunnableMethod(ioTarget, &nsIThread::Shutdown); - MOZ_ASSERT(shutdownRunnable); - - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(shutdownRunnable)); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioTarget, &nsIThread::Shutdown))); return NS_OK; } @@ -2138,7 +2134,7 @@ RemoteBlobImpl::Destroy() } nsCOMPtr destroyRunnable = - NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy); + NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy); if (mActorTarget) { destroyRunnable = @@ -2566,7 +2562,7 @@ RemoteBlobImpl::Destroy() } nsCOMPtr destroyRunnable = - NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy); + NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy); if (mActorTarget) { destroyRunnable = @@ -3422,7 +3418,7 @@ BlobChild::NoteDyingRemoteBlobImpl() // on the owning thread, so we proxy here if necessary. if (!IsOnOwningThread()) { nsCOMPtr runnable = - NS_NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlobImpl); + NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlobImpl); if (mEventTarget) { runnable = new CancelableRunnableWrapper(runnable, mEventTarget); @@ -4001,7 +3997,7 @@ BlobParent::NoteDyingRemoteBlobImpl() // on the main thread, so we proxy here if necessary. if (!IsOnOwningThread()) { nsCOMPtr runnable = - NS_NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlobImpl); + NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlobImpl); if (mEventTarget) { runnable = new CancelableRunnableWrapper(runnable, mEventTarget); diff --git a/dom/ipc/ContentBridgeChild.cpp b/dom/ipc/ContentBridgeChild.cpp index 30dbfc720f..d40d9d5a2d 100644 --- a/dom/ipc/ContentBridgeChild.cpp +++ b/dom/ipc/ContentBridgeChild.cpp @@ -34,8 +34,7 @@ ContentBridgeChild::~ContentBridgeChild() void ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { - MessageLoop::current()->PostTask( - NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy)); + MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy)); } /*static*/ ContentBridgeChild* diff --git a/dom/ipc/ContentBridgeParent.cpp b/dom/ipc/ContentBridgeParent.cpp index 608f7f2abc..e71febd056 100644 --- a/dom/ipc/ContentBridgeParent.cpp +++ b/dom/ipc/ContentBridgeParent.cpp @@ -37,8 +37,7 @@ ContentBridgeParent::ActorDestroy(ActorDestroyReason aWhy) if (os) { os->RemoveObserver(this, "content-child-shutdown"); } - MessageLoop::current()->PostTask( - NewRunnableMethod(this, &ContentBridgeParent::DeferredDestroy)); + MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeParent::DeferredDestroy)); } /*static*/ ContentBridgeParent* @@ -168,8 +167,7 @@ ContentBridgeParent::NotifyTabDestroyed() { int32_t numLiveTabs = ManagedPBrowserParent().Count(); if (numLiveTabs == 1) { - MessageLoop::current()->PostTask( - NewRunnableMethod(this, &ContentBridgeParent::Close)); + MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeParent::Close)); } } diff --git a/dom/ipc/ContentBridgeParent.h b/dom/ipc/ContentBridgeParent.h index 2094bc8251..18c46d4325 100644 --- a/dom/ipc/ContentBridgeParent.h +++ b/dom/ipc/ContentBridgeParent.h @@ -79,6 +79,12 @@ protected: mIsForBrowser = aIsForBrowser; } + void Close() + { + // Trick NewRunnableMethod + PContentBridgeParent::Close(); + } + protected: virtual bool RecvSyncMessage(const nsString& aMsg, diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 6c0c1969c1..a86afbafb2 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1575,7 +1575,7 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor, hasRunOnce = true; MOZ_ASSERT(!sFirstIdleTask); - RefPtr firstIdleTask = NewRunnableFunction(FirstIdle); + RefPtr firstIdleTask = NewCancelableRunnableFunction(FirstIdle); sFirstIdleTask = firstIdleTask; MessageLoop::current()->PostIdleTask(firstIdleTask.forget()); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 7f8505bab9..95b99a01fd 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -81,6 +81,7 @@ #include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/layers/PAPZParent.h" #include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/ImageBridgeParent.h" #include "mozilla/layers/SharedBufferManagerParent.h" #include "mozilla/layout/RenderFrameParent.h" @@ -2174,9 +2175,10 @@ ContentParent::ActorDestroy(ActorDestroyReason why) // Destroy any processes created by this ContentParent for(uint32_t i = 0; i < childIDArray.Length(); i++) { ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]); - MessageLoop::current()->PostTask( - NewRunnableMethod(cp, &ContentParent::ShutDownProcess, - SEND_SHUTDOWN_MESSAGE)); + MessageLoop::current()->PostTask(NewRunnableMethod + (cp, + &ContentParent::ShutDownProcess, + SEND_SHUTDOWN_MESSAGE)); } cpm->RemoveContentProcess(this->ChildID()); @@ -2257,9 +2259,10 @@ ContentParent::NotifyTabDestroyed(const TabId& aTabId, if (tabIds.Length() == 1) { // In the case of normal shutdown, send a shutdown message to child to // allow it to perform shutdown tasks. - MessageLoop::current()->PostTask( - NewRunnableMethod(this, &ContentParent::ShutDownProcess, - SEND_SHUTDOWN_MESSAGE)); + MessageLoop::current()->PostTask(NewRunnableMethod + (this, + &ContentParent::ShutDownProcess, + SEND_SHUTDOWN_MESSAGE)); } } @@ -2548,7 +2551,7 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority, // PBrowsers are created, because they rely on the Compositor // already being around. (Creation is async, so can't happen // on demand.) - bool useOffMainThreadCompositing = !!CompositorBridgeParent::CompositorLoop(); + bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop(); if (useOffMainThreadCompositing) { DebugOnly opened = PCompositorBridge::Open(this); MOZ_ASSERT(opened); diff --git a/dom/ipc/PreallocatedProcessManager.cpp b/dom/ipc/PreallocatedProcessManager.cpp index 8566f6c1eb..6ba07bd0cc 100644 --- a/dom/ipc/PreallocatedProcessManager.cpp +++ b/dom/ipc/PreallocatedProcessManager.cpp @@ -217,8 +217,7 @@ PreallocatedProcessManagerImpl::AllocateOnIdle() return; } - MessageLoop::current()->PostIdleTask( - NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow)); + MessageLoop::current()->PostIdleTask(NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow)); } void @@ -243,7 +242,7 @@ PreallocatedProcessManagerImpl::ScheduleDelayedNuwaFork() return; } - RefPtr task = NewRunnableMethod( + RefPtr task = NS_NewCancelableRunnableMethod( this, &PreallocatedProcessManagerImpl::DelayedNuwaFork); mPreallocateAppProcessTask = task; MessageLoop::current()->PostDelayedTask(task.forget(), diff --git a/dom/ipc/ProcessHangMonitor.cpp b/dom/ipc/ProcessHangMonitor.cpp index 39d832e768..c7171ee78f 100644 --- a/dom/ipc/ProcessHangMonitor.cpp +++ b/dom/ipc/ProcessHangMonitor.cpp @@ -230,22 +230,6 @@ public: } // namespace -template<> -struct RunnableMethodTraits -{ - typedef HangMonitorChild Class; - static void RetainCallee(Class* obj) { } - static void ReleaseCallee(Class* obj) { } -}; - -template<> -struct RunnableMethodTraits -{ - typedef HangMonitorParent Class; - static void RetainCallee(Class* obj) { } - static void ReleaseCallee(Class* obj) { } -}; - /* HangMonitorChild implementation */ HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor) @@ -303,8 +287,7 @@ HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy) // We use a task here to ensure that IPDL is finished with this // HangMonitorChild before it gets deleted on the main thread. - MonitorLoop()->PostTask( - NewRunnableMethod(this, &HangMonitorChild::ShutdownOnThread)); + MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ShutdownOnThread)); } bool @@ -390,9 +373,10 @@ HangMonitorChild::NotifySlowScript(nsITabChild* aTabChild, } nsAutoCString filename(aFileName); - MonitorLoop()->PostTask( - NewRunnableMethod(this, &HangMonitorChild::NotifySlowScriptAsync, - id, filename, aLineNo)); + MonitorLoop()->PostTask(NewNonOwningRunnableMethod + (this, + &HangMonitorChild::NotifySlowScriptAsync, + id, filename, aLineNo)); return SlowScriptAction::Continue; } @@ -420,10 +404,9 @@ HangMonitorChild::NotifyPluginHang(uint32_t aPluginId) mSentReport = true; // bounce to background thread - MonitorLoop()->PostTask( - NewRunnableMethod(this, - &HangMonitorChild::NotifyPluginHangAsync, - aPluginId)); + MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, + &HangMonitorChild::NotifyPluginHangAsync, + aPluginId)); } void @@ -445,8 +428,7 @@ HangMonitorChild::ClearHang() if (mSentReport) { // bounce to background thread - MonitorLoop()->PostTask( - NewRunnableMethod(this, &HangMonitorChild::ClearHangAsync)); + MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ClearHangAsync)); MonitorAutoLock lock(mMonitor); mSentReport = false; @@ -511,8 +493,8 @@ HangMonitorParent::Shutdown() mProcess = nullptr; } - MonitorLoop()->PostTask( - NewRunnableMethod(this, &HangMonitorParent::ShutdownOnThread)); + MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, + &HangMonitorParent::ShutdownOnThread)); while (!mShutdownDone) { mMonitor.Wait(); @@ -832,8 +814,8 @@ HangMonitoredProcess::TerminateScript() return NS_ERROR_UNEXPECTED; } - ProcessHangMonitor::Get()->MonitorLoop()->PostTask( - NewRunnableMethod(mActor, &HangMonitorParent::TerminateScript)); + ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor, + &HangMonitorParent::TerminateScript)); return NS_OK; } @@ -849,8 +831,8 @@ HangMonitoredProcess::BeginStartingDebugger() return NS_ERROR_UNEXPECTED; } - ProcessHangMonitor::Get()->MonitorLoop()->PostTask( - NewRunnableMethod(mActor, &HangMonitorParent::BeginStartingDebugger)); + ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor, + &HangMonitorParent::BeginStartingDebugger)); return NS_OK; } @@ -866,8 +848,8 @@ HangMonitoredProcess::EndStartingDebugger() return NS_ERROR_UNEXPECTED; } - ProcessHangMonitor::Get()->MonitorLoop()->PostTask( - NewRunnableMethod(mActor, &HangMonitorParent::EndStartingDebugger)); + ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor, + &HangMonitorParent::EndStartingDebugger)); return NS_OK; } @@ -1045,9 +1027,13 @@ mozilla::CreateHangMonitorParent(ContentParent* aContentParent, HangMonitoredProcess* process = new HangMonitoredProcess(parent, aContentParent); parent->SetProcess(process); - monitor->MonitorLoop()->PostTask( - NewRunnableMethod(parent, &HangMonitorParent::Open, - aTransport, aOtherPid, XRE_GetIOMessageLoop())); + monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod + (parent, + &HangMonitorParent::Open, + aTransport, aOtherPid, + XRE_GetIOMessageLoop())); return parent; } @@ -1061,9 +1047,13 @@ mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport, ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate(); HangMonitorChild* child = new HangMonitorChild(monitor); - monitor->MonitorLoop()->PostTask( - NewRunnableMethod(child, &HangMonitorChild::Open, - aTransport, aOtherPid, XRE_GetIOMessageLoop())); + monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod + (child, + &HangMonitorChild::Open, + aTransport, aOtherPid, + XRE_GetIOMessageLoop())); return child; } diff --git a/dom/jsurl/nsJSProtocolHandler.cpp b/dom/jsurl/nsJSProtocolHandler.cpp index 0b7a23ecb0..d005d10b14 100644 --- a/dom/jsurl/nsJSProtocolHandler.cpp +++ b/dom/jsurl/nsJSProtocolHandler.cpp @@ -659,8 +659,7 @@ nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) method = &nsJSChannel::NotifyListener; } - nsCOMPtr ev = NS_NewRunnableMethod(this, method); - nsresult rv = NS_DispatchToCurrentThread(ev); + nsresult rv = NS_DispatchToCurrentThread(mozilla::NewRunnableMethod(this, method)); if (NS_FAILED(rv)) { loadGroup->RemoveRequest(this, nullptr, rv); diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 137d1161f9..2880fd34d9 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -220,3 +220,4 @@ RewriteYoutubeEmbed=Rewriting old-style Youtube Flash embed (%S) to iframe embed RewriteYoutubeEmbedInvalidQuery=Rewriting old-style Youtube Flash embed (%S) to iframe embed (%S). Query was invalid and removed from URL. Please update page to use iframe instead of embed/object, if possible. # LOCALIZATION NOTE: Do not translate "ServiceWorker". %1$S is the ServiceWorker scope URL. %2$S is an error string. PushMessageDecryptionFailure=The ServiceWorker for scope '%1$S' encountered an error decrypting a push message: '%2$S'. For help with encryption, please see https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Using_the_Push_API#Encryption +IIRFilterChannelCountChangeWarning=IIRFilterNode channel count changes may produce audio glitches. diff --git a/dom/media/AbstractMediaDecoder.h b/dom/media/AbstractMediaDecoder.h index f4764bbbd3..4d4a1c8110 100644 --- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -62,7 +62,7 @@ public: // Return an event that will be notified when data arrives in MediaResource. // MediaDecoderReader will register with this event to receive notifications - // in order to udpate buffer ranges. + // in order to update buffer ranges. // Return null if this decoder doesn't support the event. virtual MediaEventSource* DataArrivedEvent() { @@ -74,10 +74,9 @@ protected: public: void DispatchUpdateEstimatedMediaDuration(int64_t aDuration) { - nsCOMPtr r = - NS_NewRunnableMethodWithArg(this, &AbstractMediaDecoder::UpdateEstimatedMediaDuration, - aDuration); - NS_DispatchToMainThread(r); + NS_DispatchToMainThread(NewRunnableMethod(this, + &AbstractMediaDecoder::UpdateEstimatedMediaDuration, + aDuration)); } virtual VideoFrameContainer* GetVideoFrameContainer() = 0; diff --git a/dom/media/DOMMediaStream.cpp b/dom/media/DOMMediaStream.cpp index 34f3710e42..8a6f8f6f32 100644 --- a/dom/media/DOMMediaStream.cpp +++ b/dom/media/DOMMediaStream.cpp @@ -192,13 +192,13 @@ public: { if (aTrackEvents & TRACK_EVENT_CREATED) { nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( this, &OwnedStreamListener::DoNotifyTrackCreated, aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID); aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); } else if (aTrackEvents & TRACK_EVENT_ENDED) { nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( this, &OwnedStreamListener::DoNotifyTrackEnded, aInputStream, aInputTrackID); aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); @@ -282,7 +282,7 @@ public: { if (aTrackEvents & TRACK_EVENT_ENDED) { nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs, TrackID>( + NewRunnableMethod, TrackID>( this, &PlaybackStreamListener::DoNotifyTrackEnded, aInputStream, aInputTrackID); aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); } @@ -291,7 +291,7 @@ public: void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override { nsCOMPtr runnable = - NS_NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation); + NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation); aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); } diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 311595b5c8..d22212ef00 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -287,7 +287,7 @@ MediaDecoder::ResourceCallback::NotifyBytesConsumed(int64_t aBytes, } void -MediaDecoder::NotifyOwnerActivityChanged() +MediaDecoder::NotifyOwnerActivityChanged(bool aIsVisible) { MOZ_ASSERT(NS_IsMainThread()); @@ -295,6 +295,8 @@ MediaDecoder::NotifyOwnerActivityChanged() return; } + SetElementVisibility(aIsVisible); + UpdateDormantState(false /* aDormantTimeout */, false /* aActivity */); // Start dormant timer if necessary StartDormantTimer(); @@ -330,10 +332,10 @@ MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity) } DECODER_LOG("UpdateDormantState aTimeout=%d aActivity=%d mIsDormant=%d " - "ownerActive=%d ownerHidden=%d mIsHeuristicDormant=%d " + "ownerActive=%d mIsVisible=%d mIsHeuristicDormant=%d " "mPlayState=%s encrypted=%s", aDormantTimeout, aActivity, mIsDormant, mOwner->IsActive(), - mOwner->IsHidden(), mIsHeuristicDormant, PlayStateStr(), + mIsVisible.Ref(), mIsHeuristicDormant, PlayStateStr(), (!mInfo ? "Unknown" : (mInfo->IsEncrypted() ? "1" : "0"))); bool prevDormant = mIsDormant; @@ -350,7 +352,7 @@ MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity) // Try to enable dormant by idle heuristic, when the owner is hidden. bool prevHeuristicDormant = mIsHeuristicDormant; mIsHeuristicDormant = false; - if (IsHeuristicDormantSupported() && mOwner->IsHidden()) { + if (IsHeuristicDormantSupported() && !mIsVisible) { if (aDormantTimeout && !aActivity && (mPlayState == PLAY_STATE_PAUSED || IsEnded())) { // Enable heuristic dormant @@ -406,7 +408,7 @@ MediaDecoder::StartDormantTimer() if (mIsHeuristicDormant || mShuttingDown || - !mOwner->IsHidden() || + mIsVisible || (mPlayState != PLAY_STATE_PAUSED && !IsEnded())) { @@ -572,6 +574,8 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner) "MediaDecoder::mMediaSeekable (Canonical)") , mMediaSeekableOnlyInBufferedRanges(AbstractThread::MainThread(), false, "MediaDecoder::mMediaSeekableOnlyInBufferedRanges (Canonical)") + , mIsVisible(AbstractThread::MainThread(), !mOwner->IsHidden(), + "MediaDecoder::mIsVisible (Canonical)") , mTelemetryReported(false) { MOZ_COUNT_CTOR(MediaDecoder); @@ -1361,6 +1365,13 @@ MediaDecoder::DurationChanged() } } +void +MediaDecoder::SetElementVisibility(bool aIsVisible) +{ + MOZ_ASSERT(NS_IsMainThread()); + mIsVisible = aIsVisible; +} + void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration) { diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index a40f7d3844..c0ef7068c1 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -180,7 +180,7 @@ public: // Dormant state is a state to free all scarce media resources // (like hw video codec), did not decoding and stay dormant. // It is used to share scarece media resources in system. - virtual void NotifyOwnerActivityChanged(); + virtual void NotifyOwnerActivityChanged(bool aIsVisible); void UpdateDormantState(bool aDormantTimeout, bool aActivity); @@ -250,9 +250,6 @@ protected: 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 random seeking is supported void SetMediaSeekable(bool aMediaSeekable); // Set a flag indicating whether seeking is supported only in buffered ranges @@ -368,6 +365,9 @@ private: void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; } dom::AudioChannel GetAudioChannel() { return mAudioChannel; } + // Called from HTMLMediaElement when owner document activity changes + virtual void SetElementVisibility(bool aIsVisible); + /****** * The following methods must only be called on the main * thread. @@ -744,7 +744,7 @@ protected: // start playing back again. Mirror mPlaybackPosition; - // Used to distiguish whether the audio is producing sound. + // Used to distinguish whether the audio is producing sound. Mirror mIsAudioDataAudible; // Volume of playback. 0.0 = muted. 1.0 = full volume. @@ -806,6 +806,9 @@ protected: // True if the media is only seekable within its buffered ranges. Canonical mMediaSeekableOnlyInBufferedRanges; + // True if the decoder is visible. + Canonical mIsVisible; + public: AbstractCanonical* CanonicalDurationOrNull() override; AbstractCanonical* CanonicalVolume() { @@ -853,6 +856,9 @@ public: AbstractCanonical* CanonicalMediaSeekableOnlyInBufferedRanges() { return &mMediaSeekableOnlyInBufferedRanges; } + AbstractCanonical* CanonicalIsVisible() { + return &mIsVisible; + } private: // Notify owner when the audible state changed diff --git a/dom/media/MediaDecoderReader.cpp b/dom/media/MediaDecoderReader.cpp index 65a06c57f6..5425cfe7a4 100644 --- a/dom/media/MediaDecoderReader.cpp +++ b/dom/media/MediaDecoderReader.cpp @@ -85,8 +85,7 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder) } // Dispatch initialization that needs to happen on that task queue. - nsCOMPtr r = NS_NewRunnableMethod(this, &MediaDecoderReader::InitializationTask); - mTaskQueue->Dispatch(r.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &MediaDecoderReader::InitializationTask)); } void @@ -133,17 +132,18 @@ size_t MediaDecoderReader::SizeOfAudioQueueInFrames() return mAudioQueue.GetSize(); } -nsresult MediaDecoderReader::ResetDecode() +nsresult MediaDecoderReader::ResetDecode(TargetQueues aQueues /*= AUDIO_VIDEO*/) { VideoQueue().Reset(); - AudioQueue().Reset(); - - mAudioDiscontinuity = true; mVideoDiscontinuity = true; - - mBaseAudioPromise.RejectIfExists(CANCELED, __func__); mBaseVideoPromise.RejectIfExists(CANCELED, __func__); + if (aQueues == AUDIO_VIDEO) { + AudioQueue().Reset(); + mAudioDiscontinuity = true; + mBaseAudioPromise.RejectIfExists(CANCELED, __func__); + } + return NS_OK; } @@ -184,6 +184,10 @@ MediaDecoderReader::UpdateBuffered() mBuffered = GetBuffered(); } +void +MediaDecoderReader::VisibilityChanged() +{} + media::TimeIntervals MediaDecoderReader::GetBuffered() { diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index de4ab48aba..f0c94d5db6 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -73,6 +73,11 @@ public: CANCELED }; + enum TargetQueues { + VIDEO_ONLY, + AUDIO_VIDEO + }; + using MetadataPromise = MozPromise, ReadMetadataFailureReason, IsExclusive>; using MediaDataPromise = @@ -125,7 +130,7 @@ public: // The first samples of every stream produced after a ResetDecode() call // *must* be marked as "discontinuities". If it's not, seeking work won't // properly! - virtual nsresult ResetDecode(); + virtual nsresult ResetDecode(TargetQueues aQueues = AUDIO_VIDEO); // Requests one audio sample from the reader. // @@ -395,6 +400,8 @@ private: // Recomputes mBuffered. virtual void UpdateBuffered(); + virtual void VisibilityChanged(); + virtual void NotifyDataArrivedInternal() {} // Overrides of this function should decodes an unspecified amount of diff --git a/dom/media/MediaDecoderReaderWrapper.cpp b/dom/media/MediaDecoderReaderWrapper.cpp index 4bed533f1c..ae3b8d6186 100644 --- a/dom/media/MediaDecoderReaderWrapper.cpp +++ b/dom/media/MediaDecoderReaderWrapper.cpp @@ -316,7 +316,7 @@ MediaDecoderReaderWrapper::ReleaseMediaResources() { MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); nsCOMPtr r = - NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources); + NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources); mReader->OwnerThread()->Dispatch(r.forget()); } @@ -325,12 +325,12 @@ MediaDecoderReaderWrapper::SetIdle() { MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); nsCOMPtr r = - NS_NewRunnableMethod(mReader, &MediaDecoderReader::SetIdle); + NewRunnableMethod(mReader, &MediaDecoderReader::SetIdle); mReader->OwnerThread()->Dispatch(r.forget()); } void -MediaDecoderReaderWrapper::ResetDecode() +MediaDecoderReaderWrapper::ResetDecode(TargetQueues aQueues) { MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); @@ -338,7 +338,9 @@ MediaDecoderReaderWrapper::ResetDecode() mVideoDataRequest.DisconnectIfExists(); nsCOMPtr r = - NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode); + NewRunnableMethod(mReader, + &MediaDecoderReader::ResetDecode, + aQueues); mReader->OwnerThread()->Dispatch(r.forget()); } diff --git a/dom/media/MediaDecoderReaderWrapper.h b/dom/media/MediaDecoderReaderWrapper.h index 231591ebee..34c2c64b1d 100644 --- a/dom/media/MediaDecoderReaderWrapper.h +++ b/dom/media/MediaDecoderReaderWrapper.h @@ -32,6 +32,7 @@ class MediaDecoderReaderWrapper { typedef MediaDecoderReader::SeekPromise SeekPromise; typedef MediaDecoderReader::WaitForDataPromise WaitForDataPromise; typedef MediaDecoderReader::BufferedUpdatePromise BufferedUpdatePromise; + typedef MediaDecoderReader::TargetQueues TargetQueues; NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReaderWrapper); /* @@ -255,7 +256,7 @@ public: void ReleaseMediaResources(); void SetIdle(); - void ResetDecode(); + void ResetDecode(TargetQueues aQueues); nsresult Init() { return mReader->Init(); } bool IsWaitForDataSupported() const { return mReader->IsWaitForDataSupported(); } diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index c733a24438..5202e9b040 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -122,11 +122,6 @@ static const int AUDIO_DURATION_USECS = 40000; // increase it by more. static const int THRESHOLD_FACTOR = 2; -// When the continuous silent data is over this threshold, means the a/v does -// not produce any sound. This time is decided by UX suggestion, see -// https://bugzilla.mozilla.org/show_bug.cgi?id=1235612#c18 -static const uint32_t SILENT_DATA_THRESHOLD_USECS = 10000000; - namespace detail { // If we have less than this much undecoded data available, we'll consider @@ -206,6 +201,21 @@ static void InitVideoQueuePrefs() { } } +static bool sSuspendBackgroundVideos = true; + +static void +InitSuspendBackgroundPref() +{ + MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); + + static bool sSetupPrefCache = false; + if (!sSetupPrefCache) { + sSetupPrefCache = true; + Preferences::AddBoolVarCache(&sSuspendBackgroundVideos, + "media.suspend-bkgnd-video.enabled", true); + } +} + MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, MediaDecoderReader* aReader, bool aRealTime) : @@ -247,7 +257,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mOutputStreamManager(new OutputStreamManager()), mResource(aDecoder->GetResource()), mAudioOffloading(false), - mSilentDataDuration(0), mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderStateMachine::mBuffered (Mirror)"), mEstimatedDuration(mTaskQueue, NullableTimeUnit(), @@ -277,6 +286,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, "MediaDecoderStateMachine::mMediaSeekable (Mirror)"), mMediaSeekableOnlyInBufferedRanges(mTaskQueue, false, "MediaDecoderStateMachine::mMediaSeekableOnlyInBufferedRanges (Mirror)"), + mIsVisible(mTaskQueue, true, "MediaDecoderStateMachine::mIsVisible (Mirror)"), mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderStateMachine::mDuration (Canonical"), mIsShutdown(mTaskQueue, false, @@ -294,6 +304,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); InitVideoQueuePrefs(); + InitSuspendBackgroundPref(); mBufferingWait = IsRealTime() ? 0 : 15; mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS; @@ -339,6 +350,7 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder) mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition()); mMediaSeekable.Connect(aDecoder->CanonicalMediaSeekable()); mMediaSeekableOnlyInBufferedRanges.Connect(aDecoder->CanonicalMediaSeekableOnlyInBufferedRanges()); + mIsVisible.Connect(aDecoder->CanonicalIsVisible()); // Initialize watchers. mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated); @@ -352,20 +364,31 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder) mWatchManager.Watch(mExplicitDuration, &MediaDecoderStateMachine::RecomputeDuration); mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration); mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged); + mWatchManager.Watch(mIsVisible, &MediaDecoderStateMachine::VisibilityChanged); // Configure MediaDecoderReaderWrapper. SetMediaDecoderReaderWrapperCallback(); } +void +MediaDecoderStateMachine::AudioAudibleChanged(bool aAudible) +{ + mIsAudioDataAudible = aAudible; +} + media::MediaSink* MediaDecoderStateMachine::CreateAudioSink() { RefPtr self = this; auto audioSinkCreator = [self] () { MOZ_ASSERT(self->OnTaskQueue()); - return new DecodedAudioDataSink( + DecodedAudioDataSink* audioSink = new DecodedAudioDataSink( self->mTaskQueue, self->mAudioQueue, self->GetMediaTime(), self->mInfo.mAudio, self->mAudioChannel); + + self->mAudibleListener = audioSink->AudibleEvent().Connect( + self->mTaskQueue, self.get(), &MediaDecoderStateMachine::AudioAudibleChanged); + return audioSink; }; return new AudioSinkWrapper(mTaskQueue, audioSinkCreator); } @@ -463,6 +486,10 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedVideo() { MOZ_ASSERT(OnTaskQueue()); + if (IsVideoDecodeSuspended()) { + return true; + } + if (VideoQueue().GetSize() == 0) { return false; } @@ -623,26 +650,6 @@ MediaDecoderStateMachine::Push(MediaData* aSample, MediaData::Type aSampleType) DispatchDecodeTasksIfNeeded(); } -void -MediaDecoderStateMachine::CheckIsAudible(const MediaData* aSample) -{ - MOZ_ASSERT(OnTaskQueue()); - MOZ_ASSERT(aSample->mType == MediaData::AUDIO_DATA); - - const AudioData* data = aSample->As(); - bool isAudible = data->IsAudible(); - if (isAudible && !mIsAudioDataAudible) { - mIsAudioDataAudible = true; - mSilentDataDuration = 0; - } else if (isAudible && mIsAudioDataAudible) { - mSilentDataDuration += data->mDuration; - if (mSilentDataDuration > SILENT_DATA_THRESHOLD_USECS) { - mIsAudioDataAudible = false; - mSilentDataDuration = 0; - } - } -} - void MediaDecoderStateMachine::OnAudioPopped(const RefPtr& aSample) { @@ -651,8 +658,6 @@ MediaDecoderStateMachine::OnAudioPopped(const RefPtr& aSample) mPlaybackOffset = std::max(mPlaybackOffset.Ref(), aSample->mOffset); UpdateNextFrameStatus(); DispatchAudioDecodeTaskIfNeeded(); - MaybeStartBuffering(); - CheckIsAudible(aSample); } void @@ -662,7 +667,6 @@ MediaDecoderStateMachine::OnVideoPopped(const RefPtr& aSample) mPlaybackOffset = std::max(mPlaybackOffset.Ref(), aSample->mOffset); UpdateNextFrameStatus(); DispatchVideoDecodeTaskIfNeeded(); - MaybeStartBuffering(); } void @@ -894,7 +898,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoder* aDecoder) MOZ_ASSERT(NS_IsMainThread()); // Dispatch initialization that needs to happen on that task queue. - nsCOMPtr r = NS_NewRunnableMethodWithArg>( + nsCOMPtr r = NewRunnableMethod>( this, &MediaDecoderStateMachine::InitializationTask, aDecoder); mTaskQueue->Dispatch(r.forget()); @@ -917,8 +921,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoder* aDecoder) nsresult rv = mReader->Init(); NS_ENSURE_SUCCESS(rv, rv); - r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::ReadMetadata); - OwnerThread()->Dispatch(r.forget()); + OwnerThread()->Dispatch(NewRunnableMethod(this, &MediaDecoderStateMachine::ReadMetadata)); return NS_OK; } @@ -1119,7 +1122,7 @@ void MediaDecoderStateMachine::RecomputeDuration() void MediaDecoderStateMachine::DispatchSetDormant(bool aDormant) { - nsCOMPtr r = NS_NewRunnableMethodWithArg( + nsCOMPtr r = NewRunnableMethod( this, &MediaDecoderStateMachine::SetDormant, aDormant); OwnerThread()->Dispatch(r.forget()); } @@ -1326,6 +1329,85 @@ void MediaDecoderStateMachine::PlayStateChanged() ScheduleStateMachine(); } +void MediaDecoderStateMachine::VisibilityChanged() +{ + MOZ_ASSERT(OnTaskQueue()); + DECODER_LOG("VisibilityChanged: is visible = %d", mIsVisible.Ref()); + + // Not suspending background videos so there's nothing to do. + if (!sSuspendBackgroundVideos) { + return; + } + + if (!HasVideo()) { + return; + } + + // If not transitioning to visible and not playing then there's + // nothing to do. + if (!mIsVisible || mPlayState != MediaDecoder::PLAY_STATE_PLAYING) { + return; + } + + // If an existing seek is in flight don't bother creating a new one to catch + // up. + if (mSeekTask || mQueuedSeek.Exists()) { + return; + } + + // Start video-only seek to the current time... + InitiateVideoDecodeRecoverySeek(); +} + +// InitiateVideoDecodeRecoverySeek is responsible for setting up a video-only +// seek using the seek task. When suspension of decoding for videos that are in +// background tabs (ie. invisible) is enabled, the audio keeps playing and when +// switching back to decoding video, it is highly desirable to not cause the +// audio to pause as the video is seeked else there be a noticeable audio glitch +// as the tab becomes visible. +void MediaDecoderStateMachine::InitiateVideoDecodeRecoverySeek() +{ + MOZ_ASSERT(OnTaskQueue()); + + SeekJob seekJob; + seekJob.mTarget = SeekTarget(GetMediaTime(), + SeekTarget::Type::AccurateVideoOnly, + MediaDecoderEventVisibility::Suppressed); + + SetState(DECODER_STATE_SEEKING); + + // Discard the existing seek task. + DiscardSeekTaskIfExist(); + + mSeekTaskRequest.DisconnectIfExists(); + + // SeekTask will register its callbacks to MediaDecoderReaderWrapper. + CancelMediaDecoderReaderWrapperCallback(); + + // Create a new SeekTask instance for the incoming seek task. + mSeekTask = SeekTask::CreateSeekTask(mDecoderID, OwnerThread(), + mReader.get(), Move(seekJob), + mInfo, Duration(), GetMediaTime()); + + mOnSeekingStart.Notify(MediaDecoderEventVisibility::Suppressed); + + // Reset our state machine and decoding pipeline before seeking. + if (mSeekTask->NeedToResetMDSM()) { + Reset(MediaDecoderReader::VIDEO_ONLY); + } + + // Do the seek. + mSeekTaskRequest.Begin( + mSeekTask->Seek(Duration())->Then(OwnerThread(), __func__, this, + &MediaDecoderStateMachine::OnSeekTaskResolved, + &MediaDecoderStateMachine::OnSeekTaskRejected)); + // Nobody is listening to this as OnSeekTaskResolved handles what is + // required but the promise needs to exist or SeekJob::Exists() will + // assert. + RefPtr unused = + mSeekTask->GetSeekJob().mPromise.Ensure(__func__); +} + void MediaDecoderStateMachine::BufferedRangeUpdated() { MOZ_ASSERT(OnTaskQueue()); @@ -1409,6 +1491,8 @@ void MediaDecoderStateMachine::StopMediaSink() MOZ_ASSERT(OnTaskQueue()); if (mMediaSink->IsStarted()) { DECODER_LOG("Stop MediaSink"); + mAudibleListener.DisconnectIfExists(); + mMediaSink->Stop(); mMediaSinkAudioPromise.DisconnectIfExists(); mMediaSinkVideoPromise.DisconnectIfExists(); @@ -1673,6 +1757,13 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued() return NS_OK; } + if (IsVideoDecodeSuspended() && !IsDecodingFirstFrame()) { + // The element is invisible and background videos should be suspended. + // If the first frame has already been decoded, don't request anymore video + // frames. + return NS_OK; + } + if (!IsVideoDecoding() || mReader->IsRequestingVidoeData() || mVideoWaitRequest.Exists()) { return NS_OK; @@ -2005,7 +2096,7 @@ MediaDecoderStateMachine::SeekCompleted() // Change state to DECODING or COMPLETED now. bool isLiveStream = mResource->IsLiveStream(); State nextState; - if (GetMediaTime() == Duration().ToMicroseconds() && !isLiveStream) { + if (newCurrentTime == Duration().ToMicroseconds() && !isLiveStream) { // Seeked to end of media, move to COMPLETED state. Note we don't do // this when playing a live stream, since the end of media will advance // once we download more data! @@ -2099,6 +2190,7 @@ MediaDecoderStateMachine::FinishShutdown() mDecoderPosition.DisconnectIfConnected(); mMediaSeekable.DisconnectIfConnected(); mMediaSeekableOnlyInBufferedRanges.DisconnectIfConnected(); + mIsVisible.DisconnectIfConnected(); mDuration.DisconnectAll(); mIsShutdown.DisconnectAll(); @@ -2154,6 +2246,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine() NS_ASSERTION(!IsPlaying() || IsStateMachineScheduled(), "Must have timer scheduled"); + MaybeStartBuffering(); return NS_OK; } @@ -2252,7 +2345,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine() } void -MediaDecoderStateMachine::Reset() +MediaDecoderStateMachine::Reset(MediaDecoderReader::TargetQueues aQueues /*= AUDIO_VIDEO*/) { MOZ_ASSERT(OnTaskQueue()); DECODER_LOG("MediaDecoderStateMachine::Reset"); @@ -2265,26 +2358,29 @@ MediaDecoderStateMachine::Reset() mState == DECODER_STATE_SEEKING || mState == DECODER_STATE_DORMANT); - // Stop the audio thread. Otherwise, MediaSink might be accessing AudioQueue - // outside of the decoder monitor while we are clearing the queue and causes - // crash for no samples to be popped. - StopMediaSink(); mDecodedVideoEndTime = 0; - mDecodedAudioEndTime = 0; - mAudioCompleted = false; mVideoCompleted = false; - AudioQueue().Reset(); VideoQueue().Reset(); + mVideoWaitRequest.DisconnectIfExists(); + + if (aQueues == MediaDecoderReader::AUDIO_VIDEO) { + // Stop the audio thread. Otherwise, MediaSink might be accessing AudioQueue + // outside of the decoder monitor while we are clearing the queue and causes + // crash for no samples to be popped. + StopMediaSink(); + mDecodedAudioEndTime = 0; + mAudioCompleted = false; + AudioQueue().Reset(); + mAudioWaitRequest.DisconnectIfExists(); + } mMetadataRequest.DisconnectIfExists(); - mAudioWaitRequest.DisconnectIfExists(); - mVideoWaitRequest.DisconnectIfExists(); mSeekTaskRequest.DisconnectIfExists(); mPlaybackOffset = 0; - mReader->ResetDecode(); + mReader->ResetDecode(aQueues); } int64_t @@ -2410,10 +2506,6 @@ void MediaDecoderStateMachine::StartBuffering() return; } - // Update playback position again before entering BUFFERING so the currentTime - // of the media element is more accurate during buffering. - UpdatePlaybackPositionPeriodically(); - if (IsPlaying()) { StopPlayback(); } @@ -2445,9 +2537,7 @@ MediaDecoderStateMachine::ScheduleStateMachine() } mDispatchedStateMachine = true; - nsCOMPtr task = - NS_NewRunnableMethod(this, &MediaDecoderStateMachine::RunStateMachine); - OwnerThread()->Dispatch(task.forget()); + OwnerThread()->Dispatch(NewRunnableMethod(this, &MediaDecoderStateMachine::RunStateMachine)); } void @@ -2490,6 +2580,12 @@ bool MediaDecoderStateMachine::IsStateMachineScheduled() const return mDispatchedStateMachine || mDelayedScheduler.IsScheduled(); } +bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const +{ + MOZ_ASSERT(OnTaskQueue()); + return sSuspendBackgroundVideos && !mIsVisible; +} + void MediaDecoderStateMachine::LogicalPlaybackRateChanged() { @@ -2706,7 +2802,7 @@ void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream, MOZ_ASSERT(NS_IsMainThread()); DECODER_LOG("AddOutputStream aStream=%p!", aStream); mOutputStreamManager->Add(aStream, aFinishWhenEnded); - nsCOMPtr r = NS_NewRunnableMethodWithArg( + nsCOMPtr r = NewRunnableMethod( this, &MediaDecoderStateMachine::SetAudioCaptured, true); OwnerThread()->Dispatch(r.forget()); } @@ -2717,7 +2813,7 @@ void MediaDecoderStateMachine::RemoveOutputStream(MediaStream* aStream) DECODER_LOG("RemoveOutputStream=%p!", aStream); mOutputStreamManager->Remove(aStream); if (mOutputStreamManager->IsEmpty()) { - nsCOMPtr r = NS_NewRunnableMethodWithArg( + nsCOMPtr r = NewRunnableMethod( this, &MediaDecoderStateMachine::SetAudioCaptured, false); OwnerThread()->Dispatch(r.forget()); } diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index f65273c522..abd0489489 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -368,7 +368,7 @@ private: // Resets all state related to decoding and playback, emptying all buffers // and aborting all pending operations on the decode task queue. - void Reset(); + void Reset(MediaDecoderReader::TargetQueues aQueues = MediaDecoderReader::AUDIO_VIDEO); protected: virtual ~MediaDecoderStateMachine(); @@ -385,7 +385,8 @@ protected: void OnAudioPopped(const RefPtr& aSample); void OnVideoPopped(const RefPtr& aSample); - void CheckIsAudible(const MediaData* aSample); + void AudioAudibleChanged(bool aAudible); + void VolumeChanged(); void LogicalPlaybackRateChanged(); void PreservesPitchChanged(); @@ -471,6 +472,9 @@ protected: // Notification method invoked when mPlayState changes. void PlayStateChanged(); + // Notification method invoked when mIsVisible changes. + void VisibilityChanged(); + // Sets internal state which causes playback of media to pause. // The decoder monitor must be held. void StopPlayback(); @@ -506,10 +510,15 @@ protected: void EnqueueFirstFrameLoadedEvent(); - // Clears any previous seeking state and initiates a new see on the decoder. + // Clears any previous seeking state and initiates a new seek on the decoder. // The decoder monitor must be held. void InitiateSeek(SeekJob aSeekJob); + // Clears any previous seeking state and initiates a video-only seek on the + // decoder to catch up the video to the current audio position, when recovering + // from video decoding being suspended in background. + void InitiateVideoDecodeRecoverySeek(); + nsresult DispatchAudioDecodeTaskIfNeeded(); // Ensures a task to decode audio has been dispatched to the decode task queue. @@ -592,6 +601,10 @@ protected: // case as it may not be needed again. bool IsPausedAndDecoderWaiting(); + // Returns true if the video decoding is suspended because the element is not + // visible + bool IsVideoDecodeSuspended() const; + // These return true if the respective stream's decode has not yet reached // the end of stream. bool IsAudioDecoding(); @@ -753,7 +766,7 @@ private: // At the start of decoding we want to "preroll" the decode until we've // got a few frames decoded before we consider whether decode is falling // behind. Otherwise our "we're falling behind" logic will trigger - // unneccessarily if we start playing as soon as the first sample is + // unnecessarily if we start playing as soon as the first sample is // decoded. These two fields store how many video frames and audio // samples we must consume before are considered to be finished prerolling. uint32_t AudioPrerollUsecs() const @@ -778,7 +791,8 @@ private: bool DonePrerollingVideo() { MOZ_ASSERT(OnTaskQueue()); - return !IsVideoDecoding() || + return !mIsVisible || + !IsVideoDecoding() || static_cast(VideoQueue().GetSize()) >= VideoPrerollFrames() * mPlaybackRate + 1; } @@ -859,7 +873,7 @@ private: // True if we've dispatched an event to the decode task queue to call // DecodeThreadRun(). We use this flag to prevent us from dispatching - // unneccessary runnables, since the decode thread runs in a loop. + // unnecessary runnables, since the decode thread runs in a loop. bool mDispatchedEventToDecode; // If this is true while we're in buffering mode, we can exit early, @@ -901,7 +915,7 @@ private: MozPromiseRequestHolder mBufferedUpdateRequest; // True if we need to call FinishDecodeFirstFrame() upon frame decoding - // successeeding. + // succeeding. bool mDecodingFirstFrame; // True if we are back from DECODER_STATE_DORMANT state and @@ -928,6 +942,7 @@ private: MediaEventListener mAudioQueueListener; MediaEventListener mVideoQueueListener; + MediaEventListener mAudibleListener; MediaEventProducerExc, nsAutoPtr, @@ -942,9 +957,6 @@ private: // Playback will not start when audio is offloading. bool mAudioOffloading; - // Duration of the continuous silent data. - uint32_t mSilentDataDuration; - #ifdef MOZ_EME void OnCDMProxyReady(RefPtr aProxy); void OnCDMProxyNotReady(); @@ -1000,6 +1012,9 @@ private: // True if the media is seekable only in buffered ranges. Mirror mMediaSeekableOnlyInBufferedRanges; + // IsVisible, mirrored from the media decoder. + Mirror mIsVisible; + // Duration of the media. This is guaranteed to be non-null after we finish // decoding the first frame. Canonical mDuration; @@ -1019,7 +1034,7 @@ private: // Current playback position in the stream in bytes. Canonical mPlaybackOffset; - // Used to distiguish whether the audio is producing sound. + // Used to distinguish whether the audio is producing sound. Canonical mIsAudioDataAudible; public: diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 9ce80a7afb..09b91eac76 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -70,6 +70,7 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, , mIsEncrypted(false) , mTrackDemuxersMayBlock(false) , mDemuxOnly(false) + , mSeekScheduled(false) , mVideoFrameContainer(aVideoFrameContainer) { MOZ_ASSERT(aDemuxer); @@ -96,7 +97,7 @@ MediaFormatReader::Shutdown() mSkipRequest.DisconnectIfExists(); if (mAudio.mDecoder) { - Flush(TrackInfo::kAudioTrack); + Reset(TrackInfo::kAudioTrack); if (mAudio.HasPromise()) { mAudio.RejectPromise(CANCELED, __func__); } @@ -113,10 +114,10 @@ MediaFormatReader::Shutdown() mAudio.mTaskQueue->AwaitShutdownAndIdle(); mAudio.mTaskQueue = nullptr; } - MOZ_ASSERT(mAudio.mPromise.IsEmpty()); + MOZ_ASSERT(!mAudio.HasPromise()); if (mVideo.mDecoder) { - Flush(TrackInfo::kVideoTrack); + Reset(TrackInfo::kVideoTrack); if (mVideo.HasPromise()) { mVideo.RejectPromise(CANCELED, __func__); } @@ -133,7 +134,7 @@ MediaFormatReader::Shutdown() mVideo.mTaskQueue->AwaitShutdownAndIdle(); mVideo.mTaskQueue = nullptr; } - MOZ_ASSERT(mVideo.mPromise.IsEmpty()); + MOZ_ASSERT(!mVideo.HasPromise()); mDemuxer = nullptr; @@ -488,7 +489,10 @@ MediaFormatReader::ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThr if (NS_FAILED(rv)) { return aSkipToNextKeyframe; } - return nextKeyframe < aTimeThreshold && nextKeyframe.ToMicroseconds() >= 0; + return (nextKeyframe < aTimeThreshold || + (mVideo.mTimeThreshold && + mVideo.mTimeThreshold.ref().mTime < aTimeThreshold)) && + nextKeyframe.ToMicroseconds() >= 0; } RefPtr @@ -520,24 +524,16 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe, } media::TimeUnit timeThreshold{media::TimeUnit::FromMicroseconds(aTimeThreshold)}; - 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__); + // Ensure we have no pending seek going as ShouldSkip could return out of date + // information. + if (!mVideo.HasInternalSeekPending() && + ShouldSkip(aSkipToNextKeyframe, timeThreshold)) { + RefPtr p = mVideo.EnsurePromise(__func__); SkipVideoDemuxToNextKeyFrame(timeThreshold); return p; } - RefPtr p = mVideo.mPromise.Ensure(__func__); + RefPtr p = mVideo.EnsurePromise(__func__); NotifyDecodingRequested(TrackInfo::kVideoTrack); return p; @@ -623,7 +619,7 @@ MediaFormatReader::RequestAudioData() return MediaDataPromise::CreateAndReject(CANCELED, __func__); } - RefPtr p = mAudio.mPromise.Ensure(__func__); + RefPtr p = mAudio.EnsurePromise(__func__); NotifyDecodingRequested(TrackInfo::kAudioTrack); return p; @@ -736,7 +732,6 @@ MediaFormatReader::NotifyDecodingRequested(TrackType aTrack) bool MediaFormatReader::NeedInput(DecoderData& aDecoder) { - MOZ_ASSERT(OnTaskQueue()); // We try to keep a few more compressed samples input than decoded samples // have been output, provided the state machine has requested we send it a // decoded sample. To account for H.264 streams which may require a longer @@ -747,6 +742,7 @@ MediaFormatReader::NeedInput(DecoderData& aDecoder) !aDecoder.mError && aDecoder.mDecodingRequested && !aDecoder.mDemuxRequest.Exists() && + !aDecoder.HasInternalSeekPending() && aDecoder.mOutput.Length() <= aDecoder.mDecodeAhead && (aDecoder.mInputExhausted || !aDecoder.mQueuedSamples.IsEmpty() || aDecoder.mTimeThreshold.isSome() || @@ -767,7 +763,7 @@ MediaFormatReader::ScheduleUpdate(TrackType aTrack) LOGV("SchedulingUpdate(%s)", TrackTypeToStr(aTrack)); decoder.mUpdateScheduled = true; RefPtr task( - NS_NewRunnableMethodWithArg(this, &MediaFormatReader::Update, aTrack)); + NewRunnableMethod(this, &MediaFormatReader::Update, aTrack)); OwnerThread()->Dispatch(task.forget()); } @@ -784,6 +780,21 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack) // Update our cached TimeRange. decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered(); + // We do not want to clear mWaitingForData while there are pending + // demuxing or seeking operations that could affect the value of this flag. + // This is in order to ensure that we will retry once they complete as we may + // now have new data that could potentially allow those operations to + // successfully complete if tried again. + if (decoder.mSeekRequest.Exists()) { + // Nothing more to do until this operation complete. + return true; + } + if (decoder.mDemuxRequest.Exists()) { + // We may have pending operations to process, so we want to continue + // after UpdateReceivedNewData returns. + return false; + } + if (decoder.mDrainComplete || decoder.mDraining) { // We do not want to clear mWaitingForData or mDemuxEOS while // a drain is in progress in order to properly complete the operation. @@ -809,20 +820,31 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack) if (decoder.mError) { return false; } - if (decoder.HasWaitingPromise()) { - MOZ_ASSERT(!decoder.HasPromise()); - LOG("We have new data. Resolving WaitingPromise"); - decoder.mWaitingPromise.Resolve(decoder.mType, __func__); - return true; - } + if (!mSeekPromise.IsEmpty()) { MOZ_ASSERT(!decoder.HasPromise()); + MOZ_DIAGNOSTIC_ASSERT(!mAudio.mTimeThreshold && !mVideo.mTimeThreshold, + "InternalSeek must have been aborted when Seek was first called"); + MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasWaitingPromise() && !mVideo.HasWaitingPromise(), + "Waiting promises must have been rejected when Seek was first called"); if (mVideo.mSeekRequest.Exists() || mAudio.mSeekRequest.Exists()) { // Already waiting for a seek to complete. Nothing more to do. return true; } LOG("Attempting Seek"); - AttemptSeek(); + ScheduleSeek(); + return true; + } + if (decoder.HasInternalSeekPending() || decoder.HasWaitingPromise()) { + if (decoder.HasInternalSeekPending()) { + LOG("Attempting Internal Seek"); + InternalSeek(aTrack, decoder.mTimeThreshold.ref()); + } + if (decoder.HasWaitingPromise()) { + MOZ_ASSERT(!decoder.HasPromise()); + LOG("We have new data. Resolving WaitingPromise"); + decoder.mWaitingPromise.Resolve(decoder.mType, __func__); + } return true; } return false; @@ -923,9 +945,9 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, decoder.mInfo = info; decoder.mLastStreamSourceID = info->GetID(); decoder.mNextStreamSourceID.reset(); - // Flush will clear our array of queued samples. So make a copy now. + // Reset will clear our array of queued samples. So make a copy now. nsTArray> samples{decoder.mQueuedSamples}; - Flush(aTrack); + Reset(aTrack); decoder.ShutdownDecoder(); if (sample->mKeyframe) { decoder.mQueuedSamples.AppendElements(Move(samples)); @@ -973,15 +995,22 @@ void MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTarget) { MOZ_ASSERT(OnTaskQueue()); + LOG("%s internal seek to %f", + TrackTypeToStr(aTrack), aTarget.mTime.ToSeconds()); + auto& decoder = GetDecoderData(aTrack); + decoder.Flush(); + decoder.ResetDemuxer(); decoder.mTimeThreshold = Some(aTarget); RefPtr self = this; - decoder.ResetDemuxer(); decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().mTime) ->Then(OwnerThread(), __func__, [self, aTrack] (media::TimeUnit aTime) { auto& decoder = self->GetDecoderData(aTrack); decoder.mSeekRequest.Complete(); + MOZ_ASSERT(decoder.mTimeThreshold, + "Seek promise must be disconnected when timethreshold is reset"); + decoder.mTimeThreshold.ref().mHasSeeked = true; self->NotifyDecodingRequested(aTrack); }, [self, aTrack] (DemuxerFailureReason aResult) { @@ -992,16 +1021,18 @@ MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTar self->NotifyWaitingForData(aTrack); break; case DemuxerFailureReason::END_OF_STREAM: + decoder.mTimeThreshold.reset(); self->NotifyEndOfStream(aTrack); break; case DemuxerFailureReason::CANCELED: case DemuxerFailureReason::SHUTDOWN: + decoder.mTimeThreshold.reset(); break; default: + decoder.mTimeThreshold.reset(); self->NotifyError(aTrack); break; } - decoder.mTimeThreshold.reset(); })); } @@ -1053,6 +1084,16 @@ MediaFormatReader::Update(TrackType aTrack) return; } + if (decoder.mSeekRequest.Exists()) { + LOGV("Seeking hasn't completed, nothing more to do"); + return; + } + + MOZ_DIAGNOSTIC_ASSERT(!decoder.HasInternalSeekPending() || + (!decoder.mOutput.Length() && + !decoder.mQueuedSamples.Length()), + "No frames can be demuxed or decoded while an internal seek is pending"); + // Record number of frames decoded and parsed. Automatically update the // stats counters using the AutoNotifyDecoded stack-based class. AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder); @@ -1066,7 +1107,7 @@ MediaFormatReader::Update(TrackType aTrack) // We have reached our internal seek target. decoder.mTimeThreshold.reset(); } - if (time < target.mTime || target.mDropTarget) { + if (time < target.mTime || (target.mDropTarget && time == target.mTime)) { LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)", TrackTypeToStr(aTrack), media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(), @@ -1124,7 +1165,7 @@ MediaFormatReader::Update(TrackType aTrack) // Now that draining has completed, we check if we have received // new data again as the result may now be different from the earlier // run. - if (UpdateReceivedNewData(aTrack)) { + if (UpdateReceivedNewData(aTrack) || decoder.mSeekRequest.Exists()) { LOGV("Nothing more to do"); return; } @@ -1174,6 +1215,9 @@ MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack) aData->mDiscontinuity = true; } + LOG("Resolved data promise for %s [%lld, %lld]", TrackTypeToStr(aTrack), + aData->mTime, aData->GetEndTime()); + if (aTrack == TrackInfo::kAudioTrack) { if (aData->mType != MediaData::RAW_DATA) { AudioData* audioData = static_cast(aData); @@ -1187,7 +1231,7 @@ MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack) mInfo.mAudio.mChannels = audioData->mChannels; } } - mAudio.mPromise.Resolve(aData, __func__); + mAudio.ResolvePromise(aData, __func__); } else if (aTrack == TrackInfo::kVideoTrack) { if (aData->mType != MediaData::RAW_DATA) { VideoData* videoData = static_cast(aData); @@ -1199,9 +1243,8 @@ MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack) mInfo.mVideo.mDisplay = videoData->mDisplay; } } - mVideo.mPromise.Resolve(aData, __func__); + mVideo.ResolvePromise(aData, __func__); } - LOG("Resolved data promise for %s", TrackTypeToStr(aTrack)); } size_t @@ -1240,13 +1283,11 @@ MediaFormatReader::WaitForData(MediaData::Type aType) } nsresult -MediaFormatReader::ResetDecode() +MediaFormatReader::ResetDecode(TargetQueues aQueues) { MOZ_ASSERT(OnTaskQueue()); LOGV(""); - mAudio.mSeekRequest.DisconnectIfExists(); - mVideo.mSeekRequest.DisconnectIfExists(); mSeekPromise.RejectIfExists(NS_OK, __func__); mSkipRequest.DisconnectIfExists(); @@ -1257,21 +1298,35 @@ MediaFormatReader::ResetDecode() // Reset miscellaneous seeking state. mPendingSeekTime.reset(); + ResetDemuxers(); + if (HasVideo()) { - mVideo.ResetDemuxer(); - Flush(TrackInfo::kVideoTrack); + Reset(TrackInfo::kVideoTrack); if (mVideo.HasPromise()) { mVideo.RejectPromise(CANCELED, __func__); } } - if (HasAudio()) { - mAudio.ResetDemuxer(); - Flush(TrackInfo::kAudioTrack); + + if (HasAudio() && aQueues == AUDIO_VIDEO) { + Reset(TrackInfo::kAudioTrack); if (mAudio.HasPromise()) { mAudio.RejectPromise(CANCELED, __func__); } } - return MediaDecoderReader::ResetDecode(); + return MediaDecoderReader::ResetDecode(aQueues); +} + +void +MediaFormatReader::ResetDemuxers() +{ + if (HasVideo()) { + mVideo.ResetDemuxer(); + mVideo.ResetState(); + } + if (HasAudio()) { + mAudio.ResetDemuxer(); + mAudio.ResetState(); + } } void @@ -1288,7 +1343,7 @@ MediaFormatReader::Output(TrackType aTrack, MediaData* aSample) } RefPtr task = - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( this, &MediaFormatReader::NotifyNewOutput, aTrack, aSample); OwnerThread()->Dispatch(task.forget()); } @@ -1297,7 +1352,7 @@ void MediaFormatReader::DrainComplete(TrackType aTrack) { RefPtr task = - NS_NewRunnableMethodWithArg( + NewRunnableMethod( this, &MediaFormatReader::NotifyDrainComplete, aTrack); OwnerThread()->Dispatch(task.forget()); } @@ -1306,7 +1361,7 @@ void MediaFormatReader::InputExhausted(TrackType aTrack) { RefPtr task = - NS_NewRunnableMethodWithArg( + NewRunnableMethod( this, &MediaFormatReader::NotifyInputExhausted, aTrack); OwnerThread()->Dispatch(task.forget()); } @@ -1315,42 +1370,50 @@ void MediaFormatReader::Error(TrackType aTrack) { RefPtr task = - NS_NewRunnableMethodWithArg( + NewRunnableMethod( this, &MediaFormatReader::NotifyError, aTrack); OwnerThread()->Dispatch(task.forget()); } void -MediaFormatReader::Flush(TrackType aTrack) +MediaFormatReader::Reset(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); - LOG("Flush(%s) BEGIN", TrackTypeToStr(aTrack)); + LOG("Reset(%s) BEGIN", TrackTypeToStr(aTrack)); auto& decoder = GetDecoderData(aTrack); - if (!decoder.mDecoder) { - decoder.ResetState(); - return; - } - decoder.mDecoder->Flush(); - // Purge the current decoder's state. - // ResetState clears mOutputRequested flag so that we ignore all output until - // the next request for more data. decoder.ResetState(); - LOG("Flush(%s) END", TrackTypeToStr(aTrack)); + decoder.Flush(); + + LOG("Reset(%s) END", TrackTypeToStr(aTrack)); } void MediaFormatReader::SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold) { MOZ_ASSERT(OnTaskQueue()); - - MOZ_ASSERT(mVideo.mDecoder); MOZ_ASSERT(mVideo.HasPromise()); - MOZ_ASSERT(!mVideo.mDecodingRequested); LOG("Skipping up to %lld", aTimeThreshold.ToMicroseconds()); + // Cancel any pending demux request and pending demuxed samples. + 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()); + + if (mVideo.mTimeThreshold) { + LOGV("Internal Seek pending, cancelling it"); + } + Reset(TrackInfo::kVideoTrack); + if (mVideo.mError) { + // We have flushed the decoder, and we are in error state, we can + // immediately reject the promise as there is nothing more to do. mVideo.RejectPromise(DECODE_ERROR, __func__); return; } @@ -1435,24 +1498,32 @@ MediaFormatReader::Seek(SeekTarget aTarget, int64_t aUnused) RefPtr p = mSeekPromise.Ensure(__func__); - RefPtr task( - NS_NewRunnableMethod(this, &MediaFormatReader::AttemptSeek)); - OwnerThread()->Dispatch(task.forget()); + ScheduleSeek(); return p; } +void +MediaFormatReader::ScheduleSeek() +{ + if (mSeekScheduled) { + return; + } + mSeekScheduled = true; + OwnerThread()->Dispatch(NewRunnableMethod(this, &MediaFormatReader::AttemptSeek)); +} + void MediaFormatReader::AttemptSeek() { MOZ_ASSERT(OnTaskQueue()); + + mSeekScheduled = false; + if (mPendingSeekTime.isNothing()) { return; } - // An internal seek may be pending due to Seek queueing multiple tasks calling - // AttemptSeek ; we can ignore those by resetting any pending demuxer's seek. - mAudio.mSeekRequest.DisconnectIfExists(); - mVideo.mSeekRequest.DisconnectIfExists(); + ResetDemuxers(); if (HasVideo()) { DoVideoSeek(); } else if (HasAudio()) { @@ -1529,9 +1600,10 @@ MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime) LOGV("Video seeked to %lld", aTime.ToMicroseconds()); mVideo.mSeekRequest.Complete(); - if (HasAudio()) { - MOZ_ASSERT(mPendingSeekTime.isSome() && mOriginalSeekTarget.isSome()); - if (mOriginalSeekTarget.ref().IsFast()) { + MOZ_ASSERT(mOriginalSeekTarget.isSome()); + if (HasAudio() && !mOriginalSeekTarget->IsVideoOnly()) { + MOZ_ASSERT(mPendingSeekTime.isSome()); + if (mOriginalSeekTarget->IsFast()) { // We are performing a fast seek. We need to seek audio to where the // video seeked to, to ensure proper A/V sync once playback resume. mPendingSeekTime = Some(aTime); @@ -1653,6 +1725,8 @@ MediaFormatReader::NotifyDemuxer() return; } + LOGV(""); + mDemuxer->NotifyDataArrived(); if (!mInitDone) { @@ -1707,12 +1781,48 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString) result += nsPrintfCString("audio decoder: %s\n", audioName); result += nsPrintfCString("audio frames decoded: %lld\n", mAudio.mNumSamplesOutputTotal); + if (HasAudio()) { + result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", + NeedInput(mAudio), mAudio.HasPromise(), + mAudio.mInputExhausted, + mAudio.mDemuxRequest.Exists(), + int(mAudio.mQueuedSamples.Length()), + mAudio.mDecodingRequested, + mAudio.mTimeThreshold + ? mAudio.mTimeThreshold.ref().mTime.ToSeconds() + : -1.0, + mAudio.mTimeThreshold + ? mAudio.mTimeThreshold.ref().mHasSeeked + : -1, + mAudio.mNumSamplesInput, mAudio.mNumSamplesOutput, + unsigned(size_t(mAudio.mSizeOfQueue)), + unsigned(mAudio.mOutput.Length()), + mAudio.mWaitingForData, mAudio.mLastStreamSourceID); + } result += nsPrintfCString("video decoder: %s\n", videoName); result += nsPrintfCString("hardware video decoding: %s\n", VideoIsHardwareAccelerated() ? "enabled" : "disabled"); result += nsPrintfCString("video frames decoded: %lld (skipped:%lld)\n", mVideo.mNumSamplesOutputTotal, mVideo.mNumSamplesSkippedTotal); + if (HasVideo()) { + result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", + NeedInput(mVideo), mVideo.HasPromise(), + mVideo.mInputExhausted, + mVideo.mDemuxRequest.Exists(), + int(mVideo.mQueuedSamples.Length()), + mVideo.mDecodingRequested, + mVideo.mTimeThreshold + ? mVideo.mTimeThreshold.ref().mTime.ToSeconds() + : -1.0, + mVideo.mTimeThreshold + ? mVideo.mTimeThreshold.ref().mHasSeeked + : -1, + mVideo.mNumSamplesInput, mVideo.mNumSamplesOutput, + unsigned(size_t(mVideo.mSizeOfQueue)), + unsigned(mVideo.mOutput.Length()), + mVideo.mWaitingForData, mVideo.mLastStreamSourceID); + } aString += NS_ConvertUTF8toUTF16(result); } diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 3207d81cf8..d7f810b1a5 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -62,7 +62,7 @@ public: // For Media Resource Management void ReleaseMediaResources() override; - nsresult ResetDecode() override; + nsresult ResetDecode(TargetQueues aQueues) override; RefPtr Shutdown() override; @@ -82,7 +82,7 @@ public: mDemuxOnly = aDemuxedOnly; return; } - nsCOMPtr r = NS_NewRunnableMethodWithArg( + nsCOMPtr r = NewRunnableMethod( this, &MediaDecoderReader::SetDemuxOnly, aDemuxedOnly); OwnerThread()->Dispatch(r.forget()); } @@ -137,12 +137,15 @@ private: : mTime(aTime) , mDropTarget(aDropTarget) , mWaiting(false) + , mHasSeeked(false) {} media::TimeUnit mTime; bool mDropTarget; bool mWaiting; + bool mHasSeeked; }; + // Perform an internal seek to aTime. If aDropTarget is true then // the first sample past the target will be dropped. void InternalSeek(TrackType aTrack, const InternalSeekTarget& aTarget); @@ -167,10 +170,11 @@ private: void Output(TrackType aType, MediaData* aSample); void InputExhausted(TrackType aTrack); void Error(TrackType aTrack); - void Flush(TrackType aTrack); + void Reset(TrackType aTrack); void DrainComplete(TrackType aTrack); bool ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold); + void ResetDemuxers(); size_t SizeOfQueue(TrackType aTrack); @@ -291,6 +295,8 @@ private: MozPromiseRequestHolder mInitPromise; // False when decoder is created. True when decoder Init() promise is resolved. bool mDecoderInitialized; + // Set when decoding can proceed. It is reset when a decoding promise is + // rejected or prior a seek operation. bool mDecodingRequested; bool mOutputRequested; bool mInputExhausted; @@ -316,20 +322,47 @@ private: uint64_t mNumSamplesOutputTotalSinceTelemetry; uint64_t mNumSamplesSkippedTotalSinceTelemetry; - // These get overriden in the templated concrete class. + // These get overridden in the templated concrete class. // Indicate if we have a pending promise for decoded frame. // Rejecting the promise will stop the reader from decoding ahead. - virtual bool HasPromise() = 0; + virtual bool HasPromise() const = 0; + virtual RefPtr EnsurePromise(const char* aMethodName) = 0; + virtual void ResolvePromise(MediaData* aData, const char* aMethodName) = 0; virtual void RejectPromise(MediaDecoderReader::NotDecodedReason aReason, const char* aMethodName) = 0; + // Clear track demuxer related data. void ResetDemuxer() { - // Clear demuxer related data. mDemuxRequest.DisconnectIfExists(); + mSeekRequest.DisconnectIfExists(); mTrackDemuxer->Reset(); + mQueuedSamples.Clear(); } + // Flush the decoder if present and reset decoding related data. + // Decoding will be suspended until mInputRequested is set again. + // Following a flush, the decoder is ready to accept any new data. + void Flush() + { + if (mDecoder) { + mDecoder->Flush(); + } + mDecodingRequested = false; + mOutputRequested = false; + mInputExhausted = false; + mOutput.Clear(); + mNumSamplesInput = 0; + mNumSamplesOutput = 0; + mSizeOfQueue = 0; + mDraining = false; + mDrainComplete = false; + } + + // Reset the state of the DecoderData, clearing all queued frames + // (pending demuxed and decoded). + // Decoding will be suspended until mInputRequested is set again. + // The track demuxer is *not* reset. void ResetState() { MOZ_ASSERT(mOwner->OnTaskQueue()); @@ -353,6 +386,11 @@ private: mNextStreamSourceID.reset(); } + bool HasInternalSeekPending() const + { + return mTimeThreshold && !mTimeThreshold.ref().mHasSeeked; + } + // Used by the MDSM for logging purposes. Atomic mSizeOfQueue; // Used by the MDSM to determine if video decoding is hardware accelerated. @@ -366,19 +404,33 @@ private: RefPtr mInfo; }; - struct DecoderDataWithPromise : public DecoderData { + class DecoderDataWithPromise : public DecoderData { + public: DecoderDataWithPromise(MediaFormatReader* aOwner, MediaData::Type aType, - uint32_t aDecodeAhead) : - DecoderData(aOwner, aType, aDecodeAhead) + uint32_t aDecodeAhead) + : DecoderData(aOwner, aType, aDecodeAhead) + , mHasPromise(false) + {} - MozPromiseHolder mPromise; + bool HasPromise() const override + { + return mHasPromise; + } - bool HasPromise() override + RefPtr EnsurePromise(const char* aMethodName) override { MOZ_ASSERT(mOwner->OnTaskQueue()); - return !mPromise.IsEmpty(); + mHasPromise = true; + return mPromise.Ensure(aMethodName); + } + + void ResolvePromise(MediaData* aData, const char* aMethodName) override + { + MOZ_ASSERT(mOwner->OnTaskQueue()); + mPromise.Resolve(aData, aMethodName); + mHasPromise = false; } void RejectPromise(MediaDecoderReader::NotDecodedReason aReason, @@ -387,7 +439,12 @@ private: MOZ_ASSERT(mOwner->OnTaskQueue()); mPromise.Reject(aReason, aMethodName); mDecodingRequested = false; + mHasPromise = false; } + + private: + MozPromiseHolder mPromise; + Atomic mHasPromise; }; DecoderDataWithPromise mAudio; @@ -451,6 +508,7 @@ private: // Seeking objects. bool IsSeeking() const { return mPendingSeekTime.isSome(); } + void ScheduleSeek(); void AttemptSeek(); void OnSeekFailed(TrackType aTrack, DemuxerFailureReason aFailure); void DoVideoSeek(); @@ -459,6 +517,7 @@ private: { OnSeekFailed(TrackType::kVideoTrack, aFailure); } + bool mSeekScheduled; void DoAudioSeek(); void OnAudioSeekCompleted(media::TimeUnit aTime); diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index e8d5db8553..7aa3b10bb3 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -277,11 +277,11 @@ public: switch (aEvent) { case EVENT_FINISHED: NS_DispatchToMainThread( - NS_NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished)); + NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished)); break; case EVENT_REMOVED: NS_DispatchToMainThread( - NS_NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved)); + NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved)); break; case EVENT_HAS_DIRECT_LISTENERS: NotifyDirectListeners(aGraph, true); diff --git a/dom/media/MediaRecorder.cpp b/dom/media/MediaRecorder.cpp index 1084aefb02..489a2756e8 100644 --- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -833,10 +833,8 @@ private: new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start"))); if (NS_FAILED(rv)) { - nsCOMPtr runnable = - NS_NewRunnableMethodWithArg(mRecorder, - &MediaRecorder::NotifyError, rv); - NS_DispatchToMainThread(runnable); + NS_DispatchToMainThread(NewRunnableMethod(mRecorder, + &MediaRecorder::NotifyError, rv)); } if (NS_FAILED(NS_DispatchToMainThread(new EncoderErrorNotifierRunnable(this)))) { MOZ_ASSERT(false, "NS_DispatchToMainThread EncoderErrorNotifierRunnable failed"); diff --git a/dom/media/MediaResource.cpp b/dom/media/MediaResource.cpp index 66cd7a01f5..b5b47bc23e 100644 --- a/dom/media/MediaResource.cpp +++ b/dom/media/MediaResource.cpp @@ -56,9 +56,8 @@ MediaResource::Destroy() delete this; return; } - nsCOMPtr destroyRunnable = - NS_NewNonOwningRunnableMethod(this, &MediaResource::Destroy); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(destroyRunnable)); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewNonOwningRunnableMethod(this, &MediaResource::Destroy))); } NS_IMPL_ADDREF(MediaResource) @@ -871,7 +870,7 @@ ChannelMediaResource::CacheClientNotifyDataReceived() return; mDataReceivedEvent = - NS_NewNonOwningRunnableMethod(this, &ChannelMediaResource::DoNotifyDataReceived); + NewNonOwningRunnableMethod(this, &ChannelMediaResource::DoNotifyDataReceived); NS_DispatchToMainThread(mDataReceivedEvent.get()); } diff --git a/dom/media/MediaStreamTrack.cpp b/dom/media/MediaStreamTrack.cpp index ff364e7fce..ee5f6028be 100644 --- a/dom/media/MediaStreamTrack.cpp +++ b/dom/media/MediaStreamTrack.cpp @@ -89,7 +89,7 @@ public: const PrincipalHandle& aNewPrincipalHandle) override { nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs>( + NewRunnableMethod>( this, &PrincipalHandleListener::DoNotifyPrincipalHandleChanged, aNewPrincipalHandle); aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); } diff --git a/dom/media/MediaTimer.cpp b/dom/media/MediaTimer.cpp index 89b14e26a8..1420d6aca1 100644 --- a/dom/media/MediaTimer.cpp +++ b/dom/media/MediaTimer.cpp @@ -38,12 +38,12 @@ MediaTimer::MediaTimer() void MediaTimer::DispatchDestroy() { - nsCOMPtr task = NS_NewNonOwningRunnableMethod(this, &MediaTimer::Destroy); // Hold a strong reference to the thread so that it doesn't get deleted in // Destroy(), which may run completely before the stack if Dispatch() begins // to unwind. nsCOMPtr thread = mThread; - nsresult rv = thread->Dispatch(task, NS_DISPATCH_NORMAL); + nsresult rv = thread->Dispatch(NewNonOwningRunnableMethod(this, &MediaTimer::Destroy), + NS_DISPATCH_NORMAL); MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); (void) rv; } @@ -97,8 +97,8 @@ MediaTimer::ScheduleUpdate() } mUpdateScheduled = true; - nsCOMPtr task = NS_NewRunnableMethod(this, &MediaTimer::Update); - nsresult rv = mThread->Dispatch(task, NS_DISPATCH_NORMAL); + nsresult rv = mThread->Dispatch(NewRunnableMethod(this, &MediaTimer::Update), + NS_DISPATCH_NORMAL); MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); (void) rv; } diff --git a/dom/media/RtspMediaResource.cpp b/dom/media/RtspMediaResource.cpp index 42a8c53bb8..8188579670 100644 --- a/dom/media/RtspMediaResource.cpp +++ b/dom/media/RtspMediaResource.cpp @@ -525,8 +525,8 @@ void RtspMediaResource::SetSuspend(bool aIsSuspend) RTSPMLOG("SetSuspend %d",aIsSuspend); nsCOMPtr runnable = - NS_NewRunnableMethodWithArg(this, &RtspMediaResource::NotifySuspend, - aIsSuspend); + NewRunnableMethod(this, &RtspMediaResource::NotifySuspend, + aIsSuspend); NS_DispatchToMainThread(runnable); } diff --git a/dom/media/SeekJob.cpp b/dom/media/SeekJob.cpp index 8c9bc94356..bc956cce1e 100644 --- a/dom/media/SeekJob.cpp +++ b/dom/media/SeekJob.cpp @@ -33,7 +33,7 @@ SeekJob& SeekJob::operator=(SeekJob&& aOther) return *this; } -bool SeekJob::Exists() +bool SeekJob::Exists() const { MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty()); return mTarget.IsValid(); diff --git a/dom/media/SeekJob.h b/dom/media/SeekJob.h index b5391d641b..de158591a3 100644 --- a/dom/media/SeekJob.h +++ b/dom/media/SeekJob.h @@ -21,7 +21,7 @@ struct SeekJob { SeekJob& operator=(SeekJob&& aOther); - bool Exists(); + bool Exists() const; void Resolve(bool aAtEnd, const char* aCallSite); diff --git a/dom/media/SeekTarget.h b/dom/media/SeekTarget.h index 0d81ac58c6..0f1a04743e 100644 --- a/dom/media/SeekTarget.h +++ b/dom/media/SeekTarget.h @@ -17,12 +17,14 @@ enum class MediaDecoderEventVisibility : int8_t { }; // Stores the seek target; the time to seek to, and whether an Accurate, -// or "Fast" (nearest keyframe) seek was requested. +// "Fast" (nearest keyframe), or "Video Only" (no audio seek) seek was +// requested. struct SeekTarget { enum Type { Invalid, PrevSyncPoint, - Accurate + Accurate, + AccurateVideoOnly, }; SeekTarget() : mEventVisibility(MediaDecoderEventVisibility::Observable) @@ -78,6 +80,9 @@ struct SeekTarget { bool IsAccurate() const { return mType == SeekTarget::Type::Accurate; } + bool IsVideoOnly() const { + return mType == SeekTarget::Type::AccurateVideoOnly; + } MediaDecoderEventVisibility mEventVisibility; @@ -85,7 +90,7 @@ private: // Seek target time. media::TimeUnit mTime; // Whether we should seek "Fast", or "Accurate". - // "Fast" seeks to the seek point preceeding mTime, whereas + // "Fast" seeks to the seek point preceding mTime, whereas // "Accurate" seeks as close as possible to mTime. Type mType; }; diff --git a/dom/media/SeekTask.cpp b/dom/media/SeekTask.cpp index 9761f58973..c8093e6142 100644 --- a/dom/media/SeekTask.cpp +++ b/dom/media/SeekTask.cpp @@ -177,7 +177,7 @@ SeekTask::GetSeekJob() } bool -SeekTask::Exists() +SeekTask::Exists() const { return mSeekJob.Exists(); } @@ -422,6 +422,7 @@ SeekTask::IsAudioSeekComplete() mSeekJob.Exists(), mDropAudioUntilNextDiscontinuity, mIsAudioQueueFinished, !!mSeekedAudioData); return !HasAudio() || + mSeekJob.mTarget.IsVideoOnly() || (Exists() && !mDropAudioUntilNextDiscontinuity && (mIsAudioQueueFinished || mSeekedAudioData)); } @@ -478,8 +479,10 @@ SeekTask::OnSeekResolved(media::TimeUnit) mSeekRequest.Complete(); // We must decode the first samples of active streams, so we can determine // the new stream time. So dispatch tasks to do that. - EnsureAudioDecodeTaskQueued(); EnsureVideoDecodeTaskQueued(); + if (!mSeekJob.mTarget.IsVideoOnly()) { + EnsureAudioDecodeTaskQueued(); + } } void diff --git a/dom/media/SeekTask.h b/dom/media/SeekTask.h index 9929b27b5e..f9196ff3dd 100644 --- a/dom/media/SeekTask.h +++ b/dom/media/SeekTask.h @@ -63,7 +63,7 @@ public: SeekJob& GetSeekJob(); - bool Exists(); + bool Exists() const; protected: SeekTask(const void* aDecoderID, diff --git a/dom/media/android/AndroidMediaReader.cpp b/dom/media/android/AndroidMediaReader.cpp index 15153cb966..0197379e7c 100644 --- a/dom/media/android/AndroidMediaReader.cpp +++ b/dom/media/android/AndroidMediaReader.cpp @@ -95,7 +95,7 @@ nsresult AndroidMediaReader::ReadMetadata(MediaInfo* aInfo, RefPtr AndroidMediaReader::Shutdown() { - ResetDecode(); + ResetDecode(AUDIO_VIDEO); if (mPlugin) { GetAndroidMediaPluginHost()->DestroyDecoder(mPlugin); mPlugin = nullptr; @@ -105,14 +105,14 @@ AndroidMediaReader::Shutdown() } // Resets all state related to decoding, emptying all buffers etc. -nsresult AndroidMediaReader::ResetDecode() +nsresult AndroidMediaReader::ResetDecode(TargetQueues aQueues) { if (mLastVideoFrame) { mLastVideoFrame = nullptr; } mSeekRequest.DisconnectIfExists(); mSeekPromise.RejectIfExists(NS_OK, __func__); - return MediaDecoderReader::ResetDecode(); + return MediaDecoderReader::ResetDecode(aQueues); } bool AndroidMediaReader::DecodeVideoFrame(bool &aKeyframeSkip, diff --git a/dom/media/android/AndroidMediaReader.h b/dom/media/android/AndroidMediaReader.h index 0f6092a0f8..1b5b0c49a3 100644 --- a/dom/media/android/AndroidMediaReader.h +++ b/dom/media/android/AndroidMediaReader.h @@ -42,7 +42,7 @@ public: AndroidMediaReader(AbstractMediaDecoder* aDecoder, const nsACString& aContentType); - nsresult ResetDecode() override; + nsresult ResetDecode(TargetQueues aQueues) override; bool DecodeAudioData() override; bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) override; diff --git a/dom/media/gmp/GMPContentParent.cpp b/dom/media/gmp/GMPContentParent.cpp index 83d523c91a..4eea05ae2d 100644 --- a/dom/media/gmp/GMPContentParent.cpp +++ b/dom/media/gmp/GMPContentParent.cpp @@ -134,8 +134,8 @@ GMPContentParent::CloseIfUnused() GeckoMediaPluginServiceChild::GetSingleton()); gmp->RemoveGMPContentParent(toClose); } - NS_DispatchToCurrentThread(NS_NewRunnableMethod(toClose, - &GMPContentParent::Close)); + NS_DispatchToCurrentThread(NewRunnableMethod(toClose, + &GMPContentParent::Close)); } } diff --git a/dom/media/gmp/GMPContentParent.h b/dom/media/gmp/GMPContentParent.h index 0003b5e1af..38fd775f7c 100644 --- a/dom/media/gmp/GMPContentParent.h +++ b/dom/media/gmp/GMPContentParent.h @@ -79,7 +79,7 @@ private: bool DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor) override; void CloseIfUnused(); - // Needed because NS_NewRunnableMethod tried to use the class that the method + // Needed because NewRunnableMethod tried to use the class that the method // lives on to store the receiver, but PGMPContentParent isn't refcounted. void Close() { diff --git a/dom/media/gmp/GMPDecryptorChild.cpp b/dom/media/gmp/GMPDecryptorChild.cpp index 0627aad15e..ebecf239e8 100644 --- a/dom/media/gmp/GMPDecryptorChild.cpp +++ b/dom/media/gmp/GMPDecryptorChild.cpp @@ -61,7 +61,8 @@ GMPDecryptorChild::CallOnGMPThread(MethodType aMethod, ParamType&&... aParams) // Use const reference when we have to. auto m = &GMPDecryptorChild::CallMethod< decltype(aMethod), typename AddConstReference::Type...>; - RefPtr t = NewRunnableMethod(this, m, aMethod, Forward(aParams)...); + RefPtr t = + dont_add_new_uses_of_this::NewRunnableMethod(this, m, aMethod, Forward(aParams)...); mPlugin->GMPMessageLoop()->PostTask(t.forget()); } } @@ -170,8 +171,10 @@ GMPDecryptorChild::Decrypted(GMPBuffer* aBuffer, GMPErr aResult) if (!ON_GMP_THREAD()) { // We should run this whole method on the GMP thread since the buffer needs // to be deleted after the SendDecrypted call. - RefPtr t = NewRunnableMethod(this, &GMPDecryptorChild::Decrypted, aBuffer, aResult); - mPlugin->GMPMessageLoop()->PostTask(t.forget()); + mPlugin->GMPMessageLoop()->PostTask(NewRunnableMethod + (this, + &GMPDecryptorChild::Decrypted, + aBuffer, aResult)); return; } diff --git a/dom/media/gmp/GMPDiskStorage.cpp b/dom/media/gmp/GMPDiskStorage.cpp new file mode 100644 index 0000000000..e67f90f7d1 --- /dev/null +++ b/dom/media/gmp/GMPDiskStorage.cpp @@ -0,0 +1,499 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "plhash.h" +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "nsAppDirectoryServiceDefs.h" +#include "GMPParent.h" +#include "gmp-storage.h" +#include "mozilla/unused.h" +#include "mozilla/Endian.h" +#include "nsClassHashtable.h" +#include "prio.h" +#include "mozIGeckoMediaPluginService.h" +#include "nsContentCID.h" +#include "nsServiceManagerUtils.h" +#include "nsISimpleEnumerator.h" + +namespace mozilla { + +#ifdef LOG +#undef LOG +#endif + +extern LogModule* GetGMPLog(); + +#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg) +#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg) + +namespace gmp { + +// We store the records for a given GMP as files in the profile dir. +// $profileDir/gmp/$platform/$gmpName/storage/$nodeId/ +static nsresult +GetGMPStorageDir(nsIFile** aTempDir, + const nsString& aGMPName, + const nsCString& aNodeId) +{ + if (NS_WARN_IF(!aTempDir)) { + return NS_ERROR_INVALID_ARG; + } + + nsCOMPtr mps = + do_GetService("@mozilla.org/gecko-media-plugin-service;1"); + if (NS_WARN_IF(!mps)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr tmpFile; + nsresult rv = mps->GetStorageDir(getter_AddRefs(tmpFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = tmpFile->Append(aGMPName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("storage")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = tmpFile->AppendNative(aNodeId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + tmpFile.forget(aTempDir); + + return NS_OK; +} + +// Disk-backed GMP storage. Records are stored in files on disk in +// the profile directory. The record name is a hash of the filename, +// and we resolve hash collisions by just adding 1 to the hash code. +// The format of records on disk is: +// 4 byte, uint32_t $recordNameLength, in little-endian byte order, +// record name (i.e. $recordNameLength bytes, no null terminator) +// record bytes (entire remainder of file) +class GMPDiskStorage : public GMPStorage { +public: + explicit GMPDiskStorage(const nsCString& aNodeId, + const nsString& aGMPName) + : mNodeId(aNodeId) + , mGMPName(aGMPName) + { + } + + ~GMPDiskStorage() { + // Close all open file handles. + for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) { + Record* record = iter.UserData(); + if (record->mFileDesc) { + PR_Close(record->mFileDesc); + record->mFileDesc = nullptr; + } + } + } + + nsresult Init() { + // Build our index of records on disk. + nsCOMPtr storageDir; + nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + DirectoryEnumerator iter(storageDir, DirectoryEnumerator::FilesAndDirs); + for (nsCOMPtr dirEntry; (dirEntry = iter.Next()) != nullptr;) { + PRFileDesc* fd = nullptr; + if (NS_FAILED(dirEntry->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) { + continue; + } + int32_t recordLength = 0; + nsCString recordName; + nsresult err = ReadRecordMetadata(fd, recordLength, recordName); + PR_Close(fd); + if (NS_FAILED(err)) { + // File is not a valid storage file. Don't index it. Delete the file, + // to make our indexing faster in future. + dirEntry->Remove(false); + continue; + } + + nsAutoString filename; + rv = dirEntry->GetLeafName(filename); + if (NS_FAILED(rv)) { + continue; + } + + mRecords.Put(recordName, new Record(filename, recordName)); + } + + return NS_OK; + } + + GMPErr Open(const nsCString& aRecordName) override + { + MOZ_ASSERT(!IsOpen(aRecordName)); + nsresult rv; + Record* record = nullptr; + if (!mRecords.Get(aRecordName, &record)) { + // New file. + nsAutoString filename; + rv = GetUnusedFilename(aRecordName, filename); + if (NS_WARN_IF(NS_FAILED(rv))) { + return GMPGenericErr; + } + record = new Record(filename, aRecordName); + mRecords.Put(aRecordName, record); + } + + MOZ_ASSERT(record); + if (record->mFileDesc) { + NS_WARNING("Tried to open already open record"); + return GMPRecordInUse; + } + + rv = OpenStorageFile(record->mFilename, ReadWrite, &record->mFileDesc); + if (NS_WARN_IF(NS_FAILED(rv))) { + return GMPGenericErr; + } + + MOZ_ASSERT(IsOpen(aRecordName)); + + return GMPNoErr; + } + + bool IsOpen(const nsCString& aRecordName) const override { + // We are open if we have a record indexed, and it has a valid + // file descriptor. + const Record* record = mRecords.Get(aRecordName); + return record && !!record->mFileDesc; + } + + GMPErr Read(const nsCString& aRecordName, + nsTArray& aOutBytes) override + { + if (!IsOpen(aRecordName)) { + return GMPClosedErr; + } + + Record* record = nullptr; + mRecords.Get(aRecordName, &record); + MOZ_ASSERT(record && !!record->mFileDesc); // IsOpen() guarantees this. + + // Our error strategy is to report records with invalid contents as + // containing 0 bytes. Zero length records are considered "deleted" by + // the GMPStorage API. + aOutBytes.SetLength(0); + + int32_t recordLength = 0; + nsCString recordName; + nsresult err = ReadRecordMetadata(record->mFileDesc, + recordLength, + recordName); + if (NS_FAILED(err) || recordLength == 0) { + // We failed to read the record metadata. Or the record is 0 length. + // Treat damaged records as empty. + // ReadRecordMetadata() could fail if the GMP opened a new record and + // tried to read it before anything was written to it.. + return GMPNoErr; + } + + if (!aRecordName.Equals(recordName)) { + NS_WARNING("Record file contains some other record's contents!"); + return GMPRecordCorrupted; + } + + // After calling ReadRecordMetadata, we should be ready to read the + // record data. + if (PR_Available(record->mFileDesc) != recordLength) { + NS_WARNING("Record file length mismatch!"); + return GMPRecordCorrupted; + } + + aOutBytes.SetLength(recordLength); + int32_t bytesRead = PR_Read(record->mFileDesc, aOutBytes.Elements(), recordLength); + return (bytesRead == recordLength) ? GMPNoErr : GMPRecordCorrupted; + } + + GMPErr Write(const nsCString& aRecordName, + const nsTArray& aBytes) override + { + if (!IsOpen(aRecordName)) { + return GMPClosedErr; + } + + Record* record = nullptr; + mRecords.Get(aRecordName, &record); + MOZ_ASSERT(record && !!record->mFileDesc); // IsOpen() guarantees this. + + // Write operations overwrite the entire record. So close it now. + PR_Close(record->mFileDesc); + record->mFileDesc = nullptr; + + // Writing 0 bytes means removing (deleting) the file. + if (aBytes.Length() == 0) { + nsresult rv = RemoveStorageFile(record->mFilename); + if (NS_WARN_IF(NS_FAILED(rv))) { + // Could not delete file -> Continue with trying to erase the contents. + } else { + return GMPNoErr; + } + } + + // Write operations overwrite the entire record. So re-open the file + // in truncate mode, to clear its contents. + if (NS_FAILED(OpenStorageFile(record->mFilename, + Truncate, + &record->mFileDesc))) { + return GMPGenericErr; + } + + // Store the length of the record name followed by the record name + // at the start of the file. + int32_t bytesWritten = 0; + char buf[sizeof(uint32_t)] = {0}; + LittleEndian::writeUint32(buf, aRecordName.Length()); + bytesWritten = PR_Write(record->mFileDesc, buf, MOZ_ARRAY_LENGTH(buf)); + if (bytesWritten != MOZ_ARRAY_LENGTH(buf)) { + NS_WARNING("Failed to write GMPStorage record name length."); + return GMPRecordCorrupted; + } + bytesWritten = PR_Write(record->mFileDesc, + aRecordName.get(), + aRecordName.Length()); + if (bytesWritten != (int32_t)aRecordName.Length()) { + NS_WARNING("Failed to write GMPStorage record name."); + return GMPRecordCorrupted; + } + + bytesWritten = PR_Write(record->mFileDesc, aBytes.Elements(), aBytes.Length()); + if (bytesWritten != (int32_t)aBytes.Length()) { + NS_WARNING("Failed to write GMPStorage record data."); + return GMPRecordCorrupted; + } + + // Try to sync the file to disk, so that in the event of a crash, + // the record is less likely to be corrupted. + PR_Sync(record->mFileDesc); + + return GMPNoErr; + } + + GMPErr GetRecordNames(nsTArray& aOutRecordNames) const override + { + for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) { + aOutRecordNames.AppendElement(iter.UserData()->mRecordName); + } + return GMPNoErr; + } + + void Close(const nsCString& aRecordName) override + { + Record* record = nullptr; + mRecords.Get(aRecordName, &record); + if (record && !!record->mFileDesc) { + PR_Close(record->mFileDesc); + record->mFileDesc = nullptr; + } + MOZ_ASSERT(!IsOpen(aRecordName)); + } + +private: + + // We store records in a file which is a hash of the record name. + // If there is a hash collision, we just keep adding 1 to the hash + // code, until we find a free slot. + nsresult GetUnusedFilename(const nsACString& aRecordName, + nsString& aOutFilename) + { + nsCOMPtr storageDir; + nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + uint64_t recordNameHash = HashString(PromiseFlatCString(aRecordName).get()); + for (int i = 0; i < 1000000; i++) { + nsCOMPtr f; + rv = storageDir->Clone(getter_AddRefs(f)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsAutoString hashStr; + hashStr.AppendInt(recordNameHash); + rv = f->Append(hashStr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + bool exists = false; + f->Exists(&exists); + if (!exists) { + // Filename not in use, we can write into this file. + aOutFilename = hashStr; + return NS_OK; + } else { + // Hash collision; just increment the hash name and try that again. + ++recordNameHash; + continue; + } + } + // Somehow, we've managed to completely fail to find a vacant file name. + // Give up. + NS_WARNING("GetUnusedFilename had extreme hash collision!"); + return NS_ERROR_FAILURE; + } + + enum OpenFileMode { ReadWrite, Truncate }; + + nsresult OpenStorageFile(const nsAString& aFileLeafName, + const OpenFileMode aMode, + PRFileDesc** aOutFD) + { + MOZ_ASSERT(aOutFD); + + nsCOMPtr f; + nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + f->Append(aFileLeafName); + + auto mode = PR_RDWR | PR_CREATE_FILE; + if (aMode == Truncate) { + mode |= PR_TRUNCATE; + } + + return f->OpenNSPRFileDesc(mode, PR_IRWXU, aOutFD); + } + + nsresult ReadRecordMetadata(PRFileDesc* aFd, + int32_t& aOutRecordLength, + nsACString& aOutRecordName) + { + int32_t offset = PR_Seek(aFd, 0, PR_SEEK_END); + PR_Seek(aFd, 0, PR_SEEK_SET); + + if (offset < 0 || offset > GMP_MAX_RECORD_SIZE) { + // Refuse to read big records, or records where we can't get a length. + return NS_ERROR_FAILURE; + } + const uint32_t fileLength = static_cast(offset); + + // At the start of the file the length of the record name is stored in a + // uint32_t (little endian byte order) followed by the record name at the + // start of the file. The record name is not null terminated. The remainder + // of the file is the record's data. + + if (fileLength < sizeof(uint32_t)) { + // Record file doesn't have enough contents to store the record name + // length. Fail. + return NS_ERROR_FAILURE; + } + + // Read length, and convert to host byte order. + uint32_t recordNameLength = 0; + char buf[sizeof(recordNameLength)] = { 0 }; + int32_t bytesRead = PR_Read(aFd, &buf, sizeof(recordNameLength)); + recordNameLength = LittleEndian::readUint32(buf); + if (sizeof(recordNameLength) != bytesRead || + recordNameLength == 0 || + recordNameLength + sizeof(recordNameLength) > fileLength || + recordNameLength > GMP_MAX_RECORD_NAME_SIZE) { + // Record file has invalid contents. Fail. + return NS_ERROR_FAILURE; + } + + nsCString recordName; + recordName.SetLength(recordNameLength); + bytesRead = PR_Read(aFd, recordName.BeginWriting(), recordNameLength); + if ((uint32_t)bytesRead != recordNameLength) { + // Read failed. + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(fileLength >= sizeof(recordNameLength) + recordNameLength); + int32_t recordLength = fileLength - (sizeof(recordNameLength) + recordNameLength); + + aOutRecordLength = recordLength; + aOutRecordName = recordName; + + // Read cursor should be positioned after the record name, before the record contents. + if (PR_Seek(aFd, 0, PR_SEEK_CUR) != (int32_t)(sizeof(recordNameLength) + recordNameLength)) { + NS_WARNING("Read cursor mismatch after ReadRecordMetadata()"); + return NS_ERROR_FAILURE; + } + + return NS_OK; + } + + nsresult RemoveStorageFile(const nsString& aFilename) + { + nsCOMPtr f; + nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = f->Append(aFilename); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return f->Remove(/* bool recursive= */ false); + } + + struct Record { + Record(const nsAString& aFilename, + const nsACString& aRecordName) + : mFilename(aFilename) + , mRecordName(aRecordName) + , mFileDesc(0) + {} + ~Record() { + MOZ_ASSERT(!mFileDesc); + } + nsString mFilename; + nsCString mRecordName; + PRFileDesc* mFileDesc; + }; + + // Hash record name to record data. + nsClassHashtable mRecords; + const nsCString mNodeId; + const nsString mGMPName; +}; + +already_AddRefed CreateGMPDiskStorage(const nsCString& aNodeId, + const nsString& aGMPName) +{ + RefPtr storage(new GMPDiskStorage(aNodeId, aGMPName)); + if (NS_FAILED(storage->Init())) { + NS_WARNING("Failed to initialize on disk GMP storage"); + return nullptr; + } + return storage.forget(); +} + +} // namespace gmp +} // namespace mozilla diff --git a/dom/media/gmp/GMPMemoryStorage.cpp b/dom/media/gmp/GMPMemoryStorage.cpp new file mode 100644 index 0000000000..dc799caa12 --- /dev/null +++ b/dom/media/gmp/GMPMemoryStorage.cpp @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "GMPStorage.h" +#include "nsClassHashtable.h" + +namespace mozilla { +namespace gmp { + +class GMPMemoryStorage : public GMPStorage { +public: + GMPErr Open(const nsCString& aRecordName) override + { + MOZ_ASSERT(!IsOpen(aRecordName)); + + Record* record = nullptr; + if (!mRecords.Get(aRecordName, &record)) { + record = new Record(); + mRecords.Put(aRecordName, record); + } + record->mIsOpen = true; + return GMPNoErr; + } + + bool IsOpen(const nsCString& aRecordName) const override { + const Record* record = mRecords.Get(aRecordName); + if (!record) { + return false; + } + return record->mIsOpen; + } + + GMPErr Read(const nsCString& aRecordName, + nsTArray& aOutBytes) override + { + const Record* record = mRecords.Get(aRecordName); + if (!record) { + return GMPGenericErr; + } + aOutBytes = record->mData; + return GMPNoErr; + } + + GMPErr Write(const nsCString& aRecordName, + const nsTArray& aBytes) override + { + Record* record = nullptr; + if (!mRecords.Get(aRecordName, &record)) { + return GMPClosedErr; + } + record->mData = aBytes; + return GMPNoErr; + } + + GMPErr GetRecordNames(nsTArray& aOutRecordNames) const override + { + for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) { + aOutRecordNames.AppendElement(iter.Key()); + } + return GMPNoErr; + } + + void Close(const nsCString& aRecordName) override + { + Record* record = nullptr; + if (!mRecords.Get(aRecordName, &record)) { + return; + } + if (!record->mData.Length()) { + // Record is empty, delete. + mRecords.Remove(aRecordName); + } else { + record->mIsOpen = false; + } + } + +private: + + struct Record { + nsTArray mData; + bool mIsOpen = false; + }; + + nsClassHashtable mRecords; +}; + +already_AddRefed CreateGMPMemoryStorage() +{ + return RefPtr(new GMPMemoryStorage()).forget(); +} + +} // namespace gmp +} // namespace mozilla diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp index d9af06adaa..a68a72f1e9 100644 --- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -507,7 +507,7 @@ GMPParent::ChildTerminated() // removed so there is no harm in not trying to remove it again. LOGD("%s::%s: GMPThread() returned nullptr.", __CLASS__, __FUNCTION__); } else { - gmpThread->Dispatch(NS_NewRunnableMethodWithArg>( + gmpThread->Dispatch(NewRunnableMethod>( mService, &GeckoMediaPluginServiceParent::PluginTerminated, self), @@ -526,7 +526,7 @@ GMPParent::DeleteProcess() mState = GMPStateClosing; Close(); } - mProcess->Delete(NS_NewRunnableMethod(this, &GMPParent::ChildTerminated)); + mProcess->Delete(NewRunnableMethod(this, &GMPParent::ChildTerminated)); LOGD("%s: Shut down process", __FUNCTION__); mProcess = nullptr; mState = GMPStateNotLoaded; diff --git a/dom/media/gmp/GMPPlatform.cpp b/dom/media/gmp/GMPPlatform.cpp index 0c7b273a24..4746795700 100644 --- a/dom/media/gmp/GMPPlatform.cpp +++ b/dom/media/gmp/GMPPlatform.cpp @@ -121,7 +121,7 @@ RunOnMainThread(GMPTask* aTask) } RefPtr r = new GMPRunnable(aTask); - sMainLoop->PostTask(NewRunnableMethod(r.get(), &GMPRunnable::Run)); + sMainLoop->PostTask(NewRunnableMethod(r, &GMPRunnable::Run)); return GMPNoErr; } @@ -253,7 +253,6 @@ GMPThreadImpl::Post(GMPTask* aTask) } RefPtr r = new GMPRunnable(aTask); - mThread.message_loop()->PostTask(NewRunnableMethod(r.get(), &GMPRunnable::Run)); } diff --git a/dom/media/gmp/GMPProcessParent.cpp b/dom/media/gmp/GMPProcessParent.cpp index 94116c56e4..ed1903bce4 100644 --- a/dom/media/gmp/GMPProcessParent.cpp +++ b/dom/media/gmp/GMPProcessParent.cpp @@ -24,13 +24,6 @@ using mozilla::gmp::GMPProcessParent; using mozilla::ipc::GeckoChildProcessHost; using base::ProcessArchitecture; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(GMPProcessParent* obj) { } - static void ReleaseCallee(GMPProcessParent* obj) { } -}; - namespace mozilla { namespace gmp { @@ -85,7 +78,7 @@ void GMPProcessParent::Delete(nsCOMPtr aCallback) { mDeletedCallback = aCallback; - XRE_GetIOMessageLoop()->PostTask(NewRunnableMethod(this, &GMPProcessParent::DoDelete)); + XRE_GetIOMessageLoop()->PostTask(NewNonOwningRunnableMethod(this, &GMPProcessParent::DoDelete)); } void diff --git a/dom/media/gmp/GMPService.cpp b/dom/media/gmp/GMPService.cpp index 683f8aeb7e..72ce9deadc 100644 --- a/dom/media/gmp/GMPService.cpp +++ b/dom/media/gmp/GMPService.cpp @@ -326,7 +326,16 @@ GeckoMediaPluginService::ShutdownGMPThread() } nsresult -GeckoMediaPluginService::GMPDispatch(nsIRunnable* event, uint32_t flags) +GeckoMediaPluginService::GMPDispatch(nsIRunnable* event, + uint32_t flags) +{ + nsCOMPtr r(event); + return GMPDispatch(r.forget()); +} + +nsresult +GeckoMediaPluginService::GMPDispatch(already_AddRefed event, + uint32_t flags) { nsCOMPtr r(event); nsCOMPtr thread; diff --git a/dom/media/gmp/GMPService.h b/dom/media/gmp/GMPService.h index 2bda87777d..6b5113621e 100644 --- a/dom/media/gmp/GMPService.h +++ b/dom/media/gmp/GMPService.h @@ -90,6 +90,7 @@ protected: UniquePtr&& aCallback) = 0; nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL); + nsresult GMPDispatch(already_AddRefed event, uint32_t flags = NS_DISPATCH_NORMAL); void ShutdownGMPThread(); Mutex mMutex; // Protects mGMPThread and mGMPThreadShutdown and some members diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp index b9e75fb78f..450ac20261 100644 --- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -419,8 +419,8 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject, NS_LITERAL_CSTRING("Dispatching UnloadPlugins")); #endif gmpThread->Dispatch( - NS_NewRunnableMethod(this, - &GeckoMediaPluginServiceParent::UnloadPlugins), + NewRunnableMethod(this, + &GeckoMediaPluginServiceParent::UnloadPlugins), NS_DISPATCH_NORMAL); #ifdef MOZ_CRASHREPORTER @@ -493,7 +493,7 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject, } else if (!strcmp("browser:purge-session-history", aTopic)) { // Clear everything! if (!aSomeData || nsDependentString(aSomeData).IsEmpty()) { - return GMPDispatch(NS_NewRunnableMethod( + return GMPDispatch(NewRunnableMethod( this, &GeckoMediaPluginServiceParent::ClearStorage)); } @@ -503,7 +503,7 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject, if (NS_FAILED(rv)) { return rv; } - return GMPDispatch(NS_NewRunnableMethodWithArg( + return GMPDispatch(NewRunnableMethod( this, &GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread, t)); } @@ -605,7 +605,7 @@ GeckoMediaPluginServiceParent::AsyncShutdownComplete(GMPParent* aParent) if (mShuttingDownOnGMPThread) { // The main thread may be waiting for async shutdown of plugins, // one of which has completed. Wake up the main thread by sending a task. - nsCOMPtr task(NS_NewRunnableMethod( + nsCOMPtr task(NewRunnableMethod( this, &GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete)); NS_DispatchToMainThread(task); } @@ -737,7 +737,7 @@ GeckoMediaPluginServiceParent::UnloadPlugins() SetAsyncShutdownPluginState(nullptr, '3', NS_LITERAL_CSTRING("Dispatching sync-shutdown-complete")); #endif - nsCOMPtr task(NS_NewRunnableMethod( + nsCOMPtr task(NewRunnableMethod( this, &GeckoMediaPluginServiceParent::NotifySyncShutdownComplete)); NS_DispatchToMainThread(task); } @@ -1300,6 +1300,17 @@ ReadSalt(nsIFile* aPath, nsACString& aOutData) } +already_AddRefed +GeckoMediaPluginServiceParent::GetMemoryStorageFor(const nsACString& aNodeId) +{ + RefPtr s; + if (!mTempGMPStorage.Get(aNodeId, getter_AddRefs(s))) { + s = CreateGMPMemoryStorage(); + mTempGMPStorage.Put(aNodeId, s); + } + return s.forget(); +} + NS_IMETHODIMP GeckoMediaPluginServiceParent::IsPersistentStorageAllowed(const nsACString& aNodeId, bool* aOutAllowed) @@ -1749,7 +1760,7 @@ NS_IMETHODIMP GeckoMediaPluginServiceParent::ForgetThisSite(const nsAString& aSite) { MOZ_ASSERT(NS_IsMainThread()); - return GMPDispatch(NS_NewRunnableMethodWithArg( + return GMPDispatch(NewRunnableMethod( this, &GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread, NS_ConvertUTF16toUTF8(aSite))); } @@ -1776,6 +1787,10 @@ GeckoMediaPluginServiceParent::ClearStorage() if (NS_FAILED(DeleteDir(path))) { NS_WARNING("Failed to delete GMP storage directory"); } + + // Clear private-browsing storage. + mTempGMPStorage.Clear(); + NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL); } diff --git a/dom/media/gmp/GMPServiceParent.h b/dom/media/gmp/GMPServiceParent.h index 3a5d0987a1..5f456c4c1a 100644 --- a/dom/media/gmp/GMPServiceParent.h +++ b/dom/media/gmp/GMPServiceParent.h @@ -14,6 +14,7 @@ #include "mozilla/Atomics.h" #include "nsThreadUtils.h" #include "mozilla/MozPromise.h" +#include "GMPStorage.h" template struct already_AddRefed; @@ -62,6 +63,8 @@ public: // GMP thread access only bool IsShuttingDown(); + already_AddRefed GetMemoryStorageFor(const nsACString& aNodeId); + private: friend class GMPServiceParent; @@ -204,6 +207,9 @@ private: Monitor mInitPromiseMonitor; MozPromiseHolder mInitPromise; bool mLoadPluginsFromDiskComplete; + + // Hashes nodeId to the hashtable of storage for that nodeId. + nsRefPtrHashtable mTempGMPStorage; }; nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData); diff --git a/dom/media/gmp/GMPStorage.h b/dom/media/gmp/GMPStorage.h new file mode 100644 index 0000000000..db3aebc8c7 --- /dev/null +++ b/dom/media/gmp/GMPStorage.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 GMPStorage_h_ +#define GMPStorage_h_ + +#include "gmp-storage.h" +#include "mozilla/AlreadyAddRefed.h" +#include "nsISupportsImpl.h" +#include "nsString.h" +#include "nsTArray.h" + +namespace mozilla { +namespace gmp { + +class GMPStorage { +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPStorage) + + virtual GMPErr Open(const nsCString& aRecordName) = 0; + virtual bool IsOpen(const nsCString& aRecordName) const = 0; + virtual GMPErr Read(const nsCString& aRecordName, + nsTArray& aOutBytes) = 0; + virtual GMPErr Write(const nsCString& aRecordName, + const nsTArray& aBytes) = 0; + virtual GMPErr GetRecordNames(nsTArray& aOutRecordNames) const = 0; + virtual void Close(const nsCString& aRecordName) = 0; +protected: + virtual ~GMPStorage() {} +}; + +already_AddRefed CreateGMPMemoryStorage(); +already_AddRefed CreateGMPDiskStorage(const nsCString& aNodeId, + const nsString& aGMPName); + +} // namespace gmp +} // namespace mozilla + +#endif \ No newline at end of file diff --git a/dom/media/gmp/GMPStorageChild.cpp b/dom/media/gmp/GMPStorageChild.cpp index 62056cd8ff..37186c16bd 100644 --- a/dom/media/gmp/GMPStorageChild.cpp +++ b/dom/media/gmp/GMPStorageChild.cpp @@ -15,7 +15,7 @@ _func(__VA_ARGS__); \ } else { \ mPlugin->GMPMessageLoop()->PostTask( \ - NewRunnableMethod(this, &GMPStorageChild::_func, ##__VA_ARGS__) \ + dont_add_new_uses_of_this::NewRunnableMethod(this, &GMPStorageChild::_func, ##__VA_ARGS__) \ ); \ } \ } while(false) diff --git a/dom/media/gmp/GMPStorageParent.cpp b/dom/media/gmp/GMPStorageParent.cpp index 5a83b5adb8..be7543bd76 100644 --- a/dom/media/gmp/GMPStorageParent.cpp +++ b/dom/media/gmp/GMPStorageParent.cpp @@ -4,20 +4,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GMPStorageParent.h" -#include "plhash.h" -#include "nsDirectoryServiceUtils.h" -#include "nsDirectoryServiceDefs.h" -#include "nsAppDirectoryServiceDefs.h" #include "GMPParent.h" #include "gmp-storage.h" #include "mozilla/unused.h" -#include "mozilla/Endian.h" -#include "nsClassHashtable.h" -#include "prio.h" #include "mozIGeckoMediaPluginService.h" -#include "nsContentCID.h" -#include "nsServiceManagerUtils.h" -#include "nsISimpleEnumerator.h" namespace mozilla { @@ -32,538 +22,6 @@ extern LogModule* GetGMPLog(); namespace gmp { -// We store the records for a given GMP as files in the profile dir. -// $profileDir/gmp/$platform/$gmpName/storage/$nodeId/ -static nsresult -GetGMPStorageDir(nsIFile** aTempDir, - const nsString& aGMPName, - const nsCString& aNodeId) -{ - if (NS_WARN_IF(!aTempDir)) { - return NS_ERROR_INVALID_ARG; - } - - nsCOMPtr mps = - do_GetService("@mozilla.org/gecko-media-plugin-service;1"); - if (NS_WARN_IF(!mps)) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr tmpFile; - nsresult rv = mps->GetStorageDir(getter_AddRefs(tmpFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = tmpFile->Append(aGMPName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); - if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("storage")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); - if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = tmpFile->AppendNative(aNodeId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); - if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - tmpFile.forget(aTempDir); - - return NS_OK; -} - -// Disk-backed GMP storage. Records are stored in files on disk in -// the profile directory. The record name is a hash of the filename, -// and we resolve hash collisions by just adding 1 to the hash code. -// The format of records on disk is: -// 4 byte, uint32_t $recordNameLength, in little-endian byte order, -// record name (i.e. $recordNameLength bytes, no null terminator) -// record bytes (entire remainder of file) -class GMPDiskStorage : public GMPStorage { -public: - explicit GMPDiskStorage(const nsCString& aNodeId, - const nsString& aGMPName) - : mNodeId(aNodeId) - , mGMPName(aGMPName) - { - } - - ~GMPDiskStorage() { - // Close all open file handles. - for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) { - Record* record = iter.UserData(); - if (record->mFileDesc) { - PR_Close(record->mFileDesc); - record->mFileDesc = nullptr; - } - } - } - - nsresult Init() { - // Build our index of records on disk. - nsCOMPtr storageDir; - nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_FAILURE; - } - - DirectoryEnumerator iter(storageDir, DirectoryEnumerator::FilesAndDirs); - for (nsCOMPtr dirEntry; (dirEntry = iter.Next()) != nullptr;) { - PRFileDesc* fd = nullptr; - if (NS_FAILED(dirEntry->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) { - continue; - } - int32_t recordLength = 0; - nsCString recordName; - nsresult err = ReadRecordMetadata(fd, recordLength, recordName); - PR_Close(fd); - if (NS_FAILED(err)) { - // File is not a valid storage file. Don't index it. Delete the file, - // to make our indexing faster in future. - dirEntry->Remove(false); - continue; - } - - nsAutoString filename; - rv = dirEntry->GetLeafName(filename); - if (NS_FAILED(rv)) { - continue; - } - - mRecords.Put(recordName, new Record(filename, recordName)); - } - - return NS_OK; - } - - GMPErr Open(const nsCString& aRecordName) override - { - MOZ_ASSERT(!IsOpen(aRecordName)); - nsresult rv; - Record* record = nullptr; - if (!mRecords.Get(aRecordName, &record)) { - // New file. - nsAutoString filename; - rv = GetUnusedFilename(aRecordName, filename); - if (NS_WARN_IF(NS_FAILED(rv))) { - return GMPGenericErr; - } - record = new Record(filename, aRecordName); - mRecords.Put(aRecordName, record); - } - - MOZ_ASSERT(record); - if (record->mFileDesc) { - NS_WARNING("Tried to open already open record"); - return GMPRecordInUse; - } - - rv = OpenStorageFile(record->mFilename, ReadWrite, &record->mFileDesc); - if (NS_WARN_IF(NS_FAILED(rv))) { - return GMPGenericErr; - } - - MOZ_ASSERT(IsOpen(aRecordName)); - - return GMPNoErr; - } - - bool IsOpen(const nsCString& aRecordName) override { - // We are open if we have a record indexed, and it has a valid - // file descriptor. - Record* record = nullptr; - return mRecords.Get(aRecordName, &record) && - !!record->mFileDesc; - } - - GMPErr Read(const nsCString& aRecordName, - nsTArray& aOutBytes) override - { - if (!IsOpen(aRecordName)) { - return GMPClosedErr; - } - - Record* record = nullptr; - mRecords.Get(aRecordName, &record); - MOZ_ASSERT(record && !!record->mFileDesc); // IsOpen() guarantees this. - - // Our error strategy is to report records with invalid contents as - // containing 0 bytes. Zero length records are considered "deleted" by - // the GMPStorage API. - aOutBytes.SetLength(0); - - int32_t recordLength = 0; - nsCString recordName; - nsresult err = ReadRecordMetadata(record->mFileDesc, - recordLength, - recordName); - if (NS_FAILED(err) || recordLength == 0) { - // We failed to read the record metadata. Or the record is 0 length. - // Treat damaged records as empty. - // ReadRecordMetadata() could fail if the GMP opened a new record and - // tried to read it before anything was written to it.. - return GMPNoErr; - } - - if (!aRecordName.Equals(recordName)) { - NS_WARNING("Record file contains some other record's contents!"); - return GMPRecordCorrupted; - } - - // After calling ReadRecordMetadata, we should be ready to read the - // record data. - if (PR_Available(record->mFileDesc) != recordLength) { - NS_WARNING("Record file length mismatch!"); - return GMPRecordCorrupted; - } - - aOutBytes.SetLength(recordLength); - int32_t bytesRead = PR_Read(record->mFileDesc, aOutBytes.Elements(), recordLength); - return (bytesRead == recordLength) ? GMPNoErr : GMPRecordCorrupted; - } - - GMPErr Write(const nsCString& aRecordName, - const nsTArray& aBytes) override - { - if (!IsOpen(aRecordName)) { - return GMPClosedErr; - } - - Record* record = nullptr; - mRecords.Get(aRecordName, &record); - MOZ_ASSERT(record && !!record->mFileDesc); // IsOpen() guarantees this. - - // Write operations overwrite the entire record. So close it now. - PR_Close(record->mFileDesc); - record->mFileDesc = nullptr; - - // Writing 0 bytes means removing (deleting) the file. - if (aBytes.Length() == 0) { - nsresult rv = RemoveStorageFile(record->mFilename); - if (NS_WARN_IF(NS_FAILED(rv))) { - // Could not delete file -> Continue with trying to erase the contents. - } else { - return GMPNoErr; - } - } - - // Write operations overwrite the entire record. So re-open the file - // in truncate mode, to clear its contents. - if (NS_FAILED(OpenStorageFile(record->mFilename, - Truncate, - &record->mFileDesc))) { - return GMPGenericErr; - } - - // Store the length of the record name followed by the record name - // at the start of the file. - int32_t bytesWritten = 0; - char buf[sizeof(uint32_t)] = {0}; - LittleEndian::writeUint32(buf, aRecordName.Length()); - bytesWritten = PR_Write(record->mFileDesc, buf, MOZ_ARRAY_LENGTH(buf)); - if (bytesWritten != MOZ_ARRAY_LENGTH(buf)) { - NS_WARNING("Failed to write GMPStorage record name length."); - return GMPRecordCorrupted; - } - bytesWritten = PR_Write(record->mFileDesc, - aRecordName.get(), - aRecordName.Length()); - if (bytesWritten != (int32_t)aRecordName.Length()) { - NS_WARNING("Failed to write GMPStorage record name."); - return GMPRecordCorrupted; - } - - bytesWritten = PR_Write(record->mFileDesc, aBytes.Elements(), aBytes.Length()); - if (bytesWritten != (int32_t)aBytes.Length()) { - NS_WARNING("Failed to write GMPStorage record data."); - return GMPRecordCorrupted; - } - - // Try to sync the file to disk, so that in the event of a crash, - // the record is less likely to be corrupted. - PR_Sync(record->mFileDesc); - - return GMPNoErr; - } - - GMPErr GetRecordNames(nsTArray& aOutRecordNames) override - { - for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) { - aOutRecordNames.AppendElement(iter.UserData()->mRecordName); - } - return GMPNoErr; - } - - void Close(const nsCString& aRecordName) override - { - Record* record = nullptr; - mRecords.Get(aRecordName, &record); - if (record && !!record->mFileDesc) { - PR_Close(record->mFileDesc); - record->mFileDesc = nullptr; - } - MOZ_ASSERT(!IsOpen(aRecordName)); - } - -private: - - // We store records in a file which is a hash of the record name. - // If there is a hash collision, we just keep adding 1 to the hash - // code, until we find a free slot. - nsresult GetUnusedFilename(const nsACString& aRecordName, - nsString& aOutFilename) - { - nsCOMPtr storageDir; - nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - uint64_t recordNameHash = HashString(PromiseFlatCString(aRecordName).get()); - for (int i = 0; i < 1000000; i++) { - nsCOMPtr f; - rv = storageDir->Clone(getter_AddRefs(f)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - nsAutoString hashStr; - hashStr.AppendInt(recordNameHash); - rv = f->Append(hashStr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - bool exists = false; - f->Exists(&exists); - if (!exists) { - // Filename not in use, we can write into this file. - aOutFilename = hashStr; - return NS_OK; - } else { - // Hash collision; just increment the hash name and try that again. - ++recordNameHash; - continue; - } - } - // Somehow, we've managed to completely fail to find a vacant file name. - // Give up. - NS_WARNING("GetUnusedFilename had extreme hash collision!"); - return NS_ERROR_FAILURE; - } - - enum OpenFileMode { ReadWrite, Truncate }; - - nsresult OpenStorageFile(const nsAString& aFileLeafName, - const OpenFileMode aMode, - PRFileDesc** aOutFD) - { - MOZ_ASSERT(aOutFD); - - nsCOMPtr f; - nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - f->Append(aFileLeafName); - - auto mode = PR_RDWR | PR_CREATE_FILE; - if (aMode == Truncate) { - mode |= PR_TRUNCATE; - } - - return f->OpenNSPRFileDesc(mode, PR_IRWXU, aOutFD); - } - - nsresult ReadRecordMetadata(PRFileDesc* aFd, - int32_t& aOutRecordLength, - nsACString& aOutRecordName) - { - int32_t offset = PR_Seek(aFd, 0, PR_SEEK_END); - PR_Seek(aFd, 0, PR_SEEK_SET); - - if (offset < 0 || offset > GMP_MAX_RECORD_SIZE) { - // Refuse to read big records, or records where we can't get a length. - return NS_ERROR_FAILURE; - } - const uint32_t fileLength = static_cast(offset); - - // At the start of the file the length of the record name is stored in a - // uint32_t (little endian byte order) followed by the record name at the - // start of the file. The record name is not null terminated. The remainder - // of the file is the record's data. - - if (fileLength < sizeof(uint32_t)) { - // Record file doesn't have enough contents to store the record name - // length. Fail. - return NS_ERROR_FAILURE; - } - - // Read length, and convert to host byte order. - uint32_t recordNameLength = 0; - char buf[sizeof(recordNameLength)] = { 0 }; - int32_t bytesRead = PR_Read(aFd, &buf, sizeof(recordNameLength)); - recordNameLength = LittleEndian::readUint32(buf); - if (sizeof(recordNameLength) != bytesRead || - recordNameLength == 0 || - recordNameLength + sizeof(recordNameLength) > fileLength || - recordNameLength > GMP_MAX_RECORD_NAME_SIZE) { - // Record file has invalid contents. Fail. - return NS_ERROR_FAILURE; - } - - nsCString recordName; - recordName.SetLength(recordNameLength); - bytesRead = PR_Read(aFd, recordName.BeginWriting(), recordNameLength); - if ((uint32_t)bytesRead != recordNameLength) { - // Read failed. - return NS_ERROR_FAILURE; - } - - MOZ_ASSERT(fileLength >= sizeof(recordNameLength) + recordNameLength); - int32_t recordLength = fileLength - (sizeof(recordNameLength) + recordNameLength); - - aOutRecordLength = recordLength; - aOutRecordName = recordName; - - // Read cursor should be positioned after the record name, before the record contents. - if (PR_Seek(aFd, 0, PR_SEEK_CUR) != (int32_t)(sizeof(recordNameLength) + recordNameLength)) { - NS_WARNING("Read cursor mismatch after ReadRecordMetadata()"); - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - nsresult RemoveStorageFile(const nsString& aFilename) - { - nsCOMPtr f; - nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = f->Append(aFilename); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return f->Remove(/* bool recursive= */ false); - } - - struct Record { - Record(const nsAString& aFilename, - const nsACString& aRecordName) - : mFilename(aFilename) - , mRecordName(aRecordName) - , mFileDesc(0) - {} - ~Record() { - MOZ_ASSERT(!mFileDesc); - } - nsString mFilename; - nsCString mRecordName; - PRFileDesc* mFileDesc; - }; - - // Hash record name to record data. - nsClassHashtable mRecords; - const nsCString mNodeId; - const nsString mGMPName; -}; - -class GMPMemoryStorage : public GMPStorage { -public: - GMPErr Open(const nsCString& aRecordName) override - { - MOZ_ASSERT(!IsOpen(aRecordName)); - - Record* record = nullptr; - if (!mRecords.Get(aRecordName, &record)) { - record = new Record(); - mRecords.Put(aRecordName, record); - } - record->mIsOpen = true; - return GMPNoErr; - } - - bool IsOpen(const nsCString& aRecordName) override { - Record* record = nullptr; - if (!mRecords.Get(aRecordName, &record)) { - return false; - } - return record->mIsOpen; - } - - GMPErr Read(const nsCString& aRecordName, - nsTArray& aOutBytes) override - { - Record* record = nullptr; - if (!mRecords.Get(aRecordName, &record)) { - return GMPGenericErr; - } - aOutBytes = record->mData; - return GMPNoErr; - } - - GMPErr Write(const nsCString& aRecordName, - const nsTArray& aBytes) override - { - Record* record = nullptr; - if (!mRecords.Get(aRecordName, &record)) { - return GMPClosedErr; - } - record->mData = aBytes; - return GMPNoErr; - } - - GMPErr GetRecordNames(nsTArray& aOutRecordNames) override - { - for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) { - aOutRecordNames.AppendElement(iter.Key()); - } - return GMPNoErr; - } - - void Close(const nsCString& aRecordName) override - { - Record* record = nullptr; - if (!mRecords.Get(aRecordName, &record)) { - return; - } - if (!record->mData.Length()) { - // Record is empty, delete. - mRecords.Remove(aRecordName); - } else { - record->mIsOpen = false; - } - } - -private: - - struct Record { - Record() : mIsOpen(false) {} - nsTArray mData; - bool mIsOpen; - }; - - nsClassHashtable mRecords; -}; - GMPStorageParent::GMPStorageParent(const nsCString& aNodeId, GMPParent* aPlugin) : mNodeId(aNodeId) @@ -580,8 +38,7 @@ GMPStorageParent::Init() if (NS_WARN_IF(mNodeId.IsEmpty())) { return NS_ERROR_FAILURE; } - nsCOMPtr mps = - do_GetService("@mozilla.org/gecko-media-plugin-service;1"); + RefPtr mps(GeckoMediaPluginServiceParent::GetSingleton()); if (NS_WARN_IF(!mps)) { return NS_ERROR_FAILURE; } @@ -591,15 +48,12 @@ GMPStorageParent::Init() return NS_ERROR_FAILURE; } if (persistent) { - UniquePtr storage = - MakeUnique(mNodeId, mPlugin->GetPluginBaseName()); - if (NS_FAILED(storage->Init())) { - NS_WARNING("Failed to initialize on disk GMP storage"); - return NS_ERROR_FAILURE; - } - mStorage = Move(storage); + mStorage = CreateGMPDiskStorage(mNodeId, mPlugin->GetPluginBaseName()); } else { - mStorage = MakeUnique(); + mStorage = mps->GetMemoryStorageFor(mNodeId); + } + if (!mStorage) { + return NS_ERROR_FAILURE; } mShutdown = false; diff --git a/dom/media/gmp/GMPStorageParent.h b/dom/media/gmp/GMPStorageParent.h index 60cbea9d8c..3d2ea69ac6 100644 --- a/dom/media/gmp/GMPStorageParent.h +++ b/dom/media/gmp/GMPStorageParent.h @@ -7,28 +7,13 @@ #define GMPStorageParent_h_ #include "mozilla/gmp/PGMPStorageParent.h" -#include "gmp-storage.h" -#include "mozilla/UniquePtr.h" +#include "GMPStorage.h" namespace mozilla { namespace gmp { class GMPParent; -class GMPStorage { -public: - virtual ~GMPStorage() {} - - virtual GMPErr Open(const nsCString& aRecordName) = 0; - virtual bool IsOpen(const nsCString& aRecordName) = 0; - virtual GMPErr Read(const nsCString& aRecordName, - nsTArray& aOutBytes) = 0; - virtual GMPErr Write(const nsCString& aRecordName, - const nsTArray& aBytes) = 0; - virtual GMPErr GetRecordNames(nsTArray& aOutRecordNames) = 0; - virtual void Close(const nsCString& aRecordName) = 0; -}; - class GMPStorageParent : public PGMPStorageParent { public: NS_INLINE_DECL_REFCOUNTING(GMPStorageParent) @@ -50,7 +35,7 @@ protected: private: ~GMPStorageParent() {} - UniquePtr mStorage; + RefPtr mStorage; const nsCString mNodeId; RefPtr mPlugin; diff --git a/dom/media/gmp/GMPVideoDecoderChild.cpp b/dom/media/gmp/GMPVideoDecoderChild.cpp index 4d0074a272..2596906287 100644 --- a/dom/media/gmp/GMPVideoDecoderChild.cpp +++ b/dom/media/gmp/GMPVideoDecoderChild.cpp @@ -226,7 +226,8 @@ GMPVideoDecoderChild::Alloc(size_t aSize, rv = CallNeedShmem(aSize, aMem); --mNeedShmemIntrCount; if (mPendingDecodeComplete) { - mPlugin->GMPMessageLoop()->PostTask(NewRunnableMethod(this, &GMPVideoDecoderChild::RecvDecodingComplete)); + mPlugin->GMPMessageLoop()->PostTask( + NewRunnableMethod(this, &GMPVideoDecoderChild::RecvDecodingComplete)); } #else #ifdef GMP_SAFE_SHMEM diff --git a/dom/media/gmp/GMPVideoEncoderChild.cpp b/dom/media/gmp/GMPVideoEncoderChild.cpp index 319450cb0d..fa139cef49 100644 --- a/dom/media/gmp/GMPVideoEncoderChild.cpp +++ b/dom/media/gmp/GMPVideoEncoderChild.cpp @@ -207,7 +207,8 @@ GMPVideoEncoderChild::Alloc(size_t aSize, rv = CallNeedShmem(aSize, aMem); --mNeedShmemIntrCount; if (mPendingEncodeComplete) { - mPlugin->GMPMessageLoop()->PostTask(NewRunnableMethod(this, &GMPVideoEncoderChild::RecvEncodingComplete)); + mPlugin->GMPMessageLoop()->PostTask( + NewRunnableMethod(this, &GMPVideoEncoderChild::RecvEncodingComplete)); } #else #ifdef GMP_SAFE_SHMEM diff --git a/dom/media/gmp/moz.build b/dom/media/gmp/moz.build index 69f68d4d28..f923ca7c53 100644 --- a/dom/media/gmp/moz.build +++ b/dom/media/gmp/moz.build @@ -52,6 +52,7 @@ EXPORTS += [ 'GMPServiceChild.h', 'GMPServiceParent.h', 'GMPSharedMemManager.h', + 'GMPStorage.h', 'GMPStorageChild.h', 'GMPStorageParent.h', 'GMPTimerChild.h', @@ -85,7 +86,9 @@ UNIFIED_SOURCES += [ 'GMPContentParent.cpp', 'GMPDecryptorChild.cpp', 'GMPDecryptorParent.cpp', + 'GMPDiskStorage.cpp', 'GMPEncryptedBufferDataImpl.cpp', + 'GMPMemoryStorage.cpp', 'GMPParent.cpp', 'GMPPlatform.cpp', 'GMPProcessChild.cpp', diff --git a/dom/media/gtest/GMPTestMonitor.h b/dom/media/gtest/GMPTestMonitor.h index d5b6d59c15..8ce6f8ddd8 100644 --- a/dom/media/gtest/GMPTestMonitor.h +++ b/dom/media/gtest/GMPTestMonitor.h @@ -36,8 +36,8 @@ private: public: void SetFinished() { - NS_DispatchToMainThread(NS_NewNonOwningRunnableMethod(this, - &GMPTestMonitor::MarkFinished)); + NS_DispatchToMainThread(mozilla::NewNonOwningRunnableMethod(this, + &GMPTestMonitor::MarkFinished)); } private: diff --git a/dom/media/gtest/TestGMPCrossOrigin.cpp b/dom/media/gtest/TestGMPCrossOrigin.cpp index 16a9f549e2..ead44cf6ed 100644 --- a/dom/media/gtest/TestGMPCrossOrigin.cpp +++ b/dom/media/gtest/TestGMPCrossOrigin.cpp @@ -304,8 +304,8 @@ EnumerateGMPStorageDir(const nsACString& aDir, T&& aDirIter) class GMPShutdownObserver : public nsIRunnable , public nsIObserver { public: - GMPShutdownObserver(nsIRunnable* aShutdownTask, - nsIRunnable* Continuation, + GMPShutdownObserver(already_AddRefed aShutdownTask, + already_AddRefed Continuation, const nsACString& aNodeId) : mShutdownTask(aShutdownTask) , mContinuation(Continuation) @@ -371,7 +371,7 @@ public: class ClearGMPStorageTask : public nsIRunnable , public nsIObserver { public: - ClearGMPStorageTask(nsIRunnable* Continuation, + ClearGMPStorageTask(already_AddRefed Continuation, nsIThread* aTarget, PRTime aSince) : mContinuation(Continuation) , mTarget(aTarget) @@ -421,11 +421,11 @@ private: NS_IMPL_ISUPPORTS(ClearGMPStorageTask, nsIRunnable, nsIObserver) static void -ClearGMPStorage(nsIRunnable* aContinuation, +ClearGMPStorage(already_AddRefed aContinuation, nsIThread* aTarget, PRTime aSince = -1) { RefPtr task( - new ClearGMPStorageTask(aContinuation, aTarget, aSince)); + new ClearGMPStorageTask(Move(aContinuation), aTarget, aSince)); NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL); } @@ -516,7 +516,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback void DoTest(void (GMPStorageTest::*aTestMethod)()) { EnsureNSSInitializedChromeOrContent(); nsCOMPtr thread(GetGMPThread()); - ClearGMPStorage(NS_NewRunnableMethod(this, aTestMethod), thread); + ClearGMPStorage(NewRunnableMethod(this, aTestMethod), thread); AwaitFinished(); } @@ -569,7 +569,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback EXPECT_TRUE(!PBnodeId2.Equals(nodeId2)); nsCOMPtr thread(GetGMPThread()); - ClearGMPStorage(NS_NewRunnableMethodWithArg( + ClearGMPStorage(NewRunnableMethod( this, &GMPStorageTest::TestGetNodeId_Continuation, nodeId1), thread); } @@ -681,7 +681,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback // It sends us a "test-storage complete" message when its passed, or // some other message if its tests fail. Expect(NS_LITERAL_CSTRING("test-storage complete"), - NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); + NewRunnableMethod(this, &GMPStorageTest::SetFinished)); CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), NS_LITERAL_STRING("http://example2.com"), @@ -700,9 +700,9 @@ class GMPStorageTest : public GMPDecryptorProxyCallback EXPECT_TRUE(IsGMPStorageIsEmpty()); // Generate storage data for some site. - nsCOMPtr r = NS_NewRunnableMethod( + nsCOMPtr r = NewRunnableMethod( this, &GMPStorageTest::TestForgetThisSite_AnotherSite); - Expect(NS_LITERAL_CSTRING("test-storage complete"), r); + Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), NS_LITERAL_STRING("http://example2.com"), @@ -714,9 +714,9 @@ class GMPStorageTest : public GMPDecryptorProxyCallback Shutdown(); // Generate storage data for another site. - nsCOMPtr r = NS_NewRunnableMethod( + nsCOMPtr r = NewRunnableMethod( this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo); - Expect(NS_LITERAL_CSTRING("test-storage complete"), r); + Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); CreateDecryptor(NS_LITERAL_STRING("http://example3.com"), NS_LITERAL_STRING("http://example4.com"), @@ -751,7 +751,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback // Collect nodeIds that are expected to remain for later comparison. EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), NodeIdCollector(siteInfo)); // Invoke "Forget this site" on the main thread. - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg>( + NS_DispatchToMainThread(NewRunnableMethod>( this, &GMPStorageTest::TestForgetThisSite_Forget, siteInfo)); } @@ -763,11 +763,11 @@ class GMPStorageTest : public GMPDecryptorProxyCallback nsCOMPtr thread; service->GetThread(getter_AddRefs(thread)); - nsCOMPtr r = NS_NewRunnableMethodWithArg>( + nsCOMPtr r = NewRunnableMethod>( this, &GMPStorageTest::TestForgetThisSite_Verify, aSiteInfo); thread->Dispatch(r, NS_DISPATCH_NORMAL); - nsCOMPtr f = NS_NewRunnableMethod( + nsCOMPtr f = NewRunnableMethod( this, &GMPStorageTest::SetFinished); thread->Dispatch(f, NS_DISPATCH_NORMAL); } @@ -833,9 +833,9 @@ class GMPStorageTest : public GMPDecryptorProxyCallback EXPECT_TRUE(IsGMPStorageIsEmpty()); // Generate storage data for some site. - nsCOMPtr r = NS_NewRunnableMethod( + nsCOMPtr r = NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory1_Clear); - Expect(NS_LITERAL_CSTRING("test-storage complete"), r); + Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), NS_LITERAL_STRING("http://example2.com"), @@ -855,9 +855,9 @@ class GMPStorageTest : public GMPDecryptorProxyCallback EXPECT_TRUE(IsGMPStorageIsEmpty()); // Generate storage data for some site. - nsCOMPtr r = NS_NewRunnableMethod( + nsCOMPtr r = NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory2_Clear); - Expect(NS_LITERAL_CSTRING("test-storage complete"), r); + Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), NS_LITERAL_STRING("http://example2.com"), @@ -877,9 +877,9 @@ class GMPStorageTest : public GMPDecryptorProxyCallback EXPECT_TRUE(IsGMPStorageIsEmpty()); // Generate storage data for some site. - nsCOMPtr r = NS_NewRunnableMethod( + nsCOMPtr r = NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory3_Clear); - Expect(NS_LITERAL_CSTRING("test-storage complete"), r); + Expect(NS_LITERAL_CSTRING("test-storage complete"), r.forget()); CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), NS_LITERAL_STRING("http://example2.com"), @@ -908,10 +908,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), f); EXPECT_TRUE(NS_SUCCEEDED(rv)); - nsCOMPtr r = NS_NewRunnableMethod( + nsCOMPtr r = NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory_CheckEmpty); nsCOMPtr t(GetGMPThread()); - ClearGMPStorage(r, t, f.GetResult()); + ClearGMPStorage(r.forget(), t, f.GetResult()); } void TestClearRecentHistory2_Clear() { @@ -919,10 +919,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), f); EXPECT_TRUE(NS_SUCCEEDED(rv)); - nsCOMPtr r = NS_NewRunnableMethod( + nsCOMPtr r = NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory_CheckEmpty); nsCOMPtr t(GetGMPThread()); - ClearGMPStorage(r, t, f.GetResult()); + ClearGMPStorage(r.forget(), t, f.GetResult()); } void TestClearRecentHistory3_Clear() { @@ -930,10 +930,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), f); EXPECT_TRUE(NS_SUCCEEDED(rv)); - nsCOMPtr r = NS_NewRunnableMethod( + nsCOMPtr r = NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory_CheckNonEmpty); nsCOMPtr t(GetGMPThread()); - ClearGMPStorage(r, t, f.GetResult() + 1); + ClearGMPStorage(r.forget(), t, f.GetResult() + 1); } class FileCounter { @@ -987,7 +987,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback auto t = time(0); nsCString response("stored crossOriginTestRecordId "); response.AppendInt((int64_t)t); - Expect(response, NS_NewRunnableMethod(this, + Expect(response, NewRunnableMethod(this, &GMPStorageTest::TestCrossOriginStorage_RecordStoredContinuation)); nsCString update("store crossOriginTestRecordId "); @@ -1007,7 +1007,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback Shutdown(); Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"), - NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); + NewRunnableMethod(this, &GMPStorageTest::SetFinished)); CreateDecryptor(NS_LITERAL_STRING("http://example5.com"), NS_LITERAL_STRING("http://example6.com"), @@ -1019,7 +1019,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback // Send the decryptor the message "store recordid $time" // Wait for the decrytor to send us "stored recordid $time" nsCString response("stored pbdata test-pb-data"); - Expect(response, NS_NewRunnableMethod(this, + Expect(response, NewRunnableMethod(this, &GMPStorageTest::TestPBStorage_RecordStoredContinuation)); // Open decryptor on one, origin, write a record, close decryptor, @@ -1036,7 +1036,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback Shutdown(); Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"), - NS_NewRunnableMethod(this, + NewRunnableMethod(this, &GMPStorageTest::TestPBStorage_RecordRetrievedContinuation)); CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"), @@ -1050,7 +1050,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback SimulatePBModeExit(); Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"), - NS_NewRunnableMethod(this, + NewRunnableMethod(this, &GMPStorageTest::SetFinished)); CreateDecryptor(NS_LITERAL_STRING("http://pb1.com"), @@ -1073,10 +1073,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback const nsAString& aOrigin2, void (GMPStorageTest::*aCallback)()) { nsCOMPtr continuation( - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( this, &GMPStorageTest::NextAsyncShutdownTimeoutTest, - NS_NewRunnableMethod(this, aCallback))); + NewRunnableMethod(this, aCallback))); CreateDecryptor(aOrigin1, aOrigin2, false, continuation); } @@ -1115,7 +1115,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback // the token. nsCString response("shutdown-token received "); response.Append(token); - Expect(response, NS_NewRunnableMethodWithArg(this, + Expect(response, NewRunnableMethod(this, &GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token)); // Test that a GMP can write to storage during shutdown, and retrieve @@ -1127,7 +1127,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback } void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) { - ShutdownThen(NS_NewRunnableMethodWithArg(this, + ShutdownThen(NewRunnableMethod(this, &GMPStorageTest::TestAsyncShutdownStorage_AsyncShutdownComplete, aToken)); } @@ -1137,7 +1137,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback nsCString response("retrieved shutdown-token "); response.Append(aToken); Expect(response, - NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); + NewRunnableMethod(this, &GMPStorageTest::SetFinished)); CreateDecryptor(NS_LITERAL_STRING("http://example13.com"), NS_LITERAL_STRING("http://example14.com"), @@ -1150,7 +1150,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback Shutdown(); Expect(NS_LITERAL_CSTRING("OP tests completed"), - NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); + NewRunnableMethod(this, &GMPStorageTest::SetFinished)); CreateDecryptor(NS_LITERAL_STRING("http://example15.com"), NS_LITERAL_STRING("http://example16.com"), @@ -1161,7 +1161,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback void TestPluginVoucher() { Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"), - NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); + NewRunnableMethod(this, &GMPStorageTest::SetFinished)); CreateDecryptor(NS_LITERAL_STRING("http://example17.com"), NS_LITERAL_STRING("http://example18.com"), @@ -1205,12 +1205,12 @@ class GMPStorageTest : public GMPDecryptorProxyCallback update.AppendLiteral(" test-data"); AppendIntPadded(update, i); - nsIRunnable* continuation = nullptr; + nsCOMPtr continuation; if (i + 1 == num) { continuation = - NS_NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames); + NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames); } - Expect(response, continuation); + Expect(response, continuation.forget()); } CreateDecryptor(NS_LITERAL_STRING("http://foo.com"), @@ -1223,7 +1223,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback nsCString response("record-names "); response.Append(mRecordNames); Expect(response, - NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); + NewRunnableMethod(this, &GMPStorageTest::SetFinished)); Update(NS_LITERAL_CSTRING("retrieve-record-names")); } @@ -1260,7 +1260,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback response.Append(longRecordName); response.AppendLiteral(" "); response.Append(data); - Expect(response, NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); + Expect(response, NewRunnableMethod(this, &GMPStorageTest::SetFinished)); nsCString update("store "); update.Append(longRecordName); @@ -1272,8 +1272,8 @@ class GMPStorageTest : public GMPDecryptorProxyCallback update); } - void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) { - mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation)); + void Expect(const nsCString& aMessage, already_AddRefed aContinuation) { + mExpected.AppendElement(ExpectedMessage(aMessage, Move(aContinuation))); } void AwaitFinished() { @@ -1283,15 +1283,15 @@ class GMPStorageTest : public GMPDecryptorProxyCallback mFinished = false; } - void ShutdownThen(nsIRunnable* aContinuation) { + void ShutdownThen(already_AddRefed aContinuation) { EXPECT_TRUE(!!mDecryptor); if (!mDecryptor) { return; } EXPECT_FALSE(mNodeId.IsEmpty()); RefPtr task( - new GMPShutdownObserver(NS_NewRunnableMethod(this, &GMPStorageTest::Shutdown), - aContinuation, mNodeId)); + new GMPShutdownObserver(NewRunnableMethod(this, &GMPStorageTest::Shutdown), + Move(aContinuation), mNodeId)); NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL); } @@ -1309,7 +1309,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback void SetFinished() { mFinished = true; Shutdown(); - NS_DispatchToMainThread(NS_NewRunnableMethod(this, &GMPStorageTest::Dummy)); + NS_DispatchToMainThread(NewRunnableMethod(this, &GMPStorageTest::Dummy)); } void SessionMessage(const nsCString& aSessionId, @@ -1364,7 +1364,7 @@ private: ~GMPStorageTest() { } struct ExpectedMessage { - ExpectedMessage(const nsCString& aMessage, nsIRunnable* aContinuation) + ExpectedMessage(const nsCString& aMessage, already_AddRefed aContinuation) : mMessage(aMessage) , mContinuation(aContinuation) {} @@ -1386,9 +1386,9 @@ GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&)) nsCOMPtr thread(GetGMPThread()); GMPTestMonitor monitor; - thread->Dispatch(NS_NewRunnableMethodWithArg(this, - aTestMethod, - monitor), + thread->Dispatch(NewRunnableMethod(this, + aTestMethod, + monitor), NS_DISPATCH_NORMAL); monitor.AwaitFinished(); } diff --git a/dom/media/gtest/TestGMPRemoveAndDelete.cpp b/dom/media/gtest/TestGMPRemoveAndDelete.cpp index ef6b8b22d1..ffedf4d30e 100644 --- a/dom/media/gtest/TestGMPRemoveAndDelete.cpp +++ b/dom/media/gtest/TestGMPRemoveAndDelete.cpp @@ -260,7 +260,7 @@ GMPRemoveTest::CreateVideoDecoder(nsCString aNodeId) GMPVideoDecoderProxy* decoder = nullptr; mGMPThread->Dispatch( - NS_NewNonOwningRunnableMethodWithArgs( + NewNonOwningRunnableMethod( this, &GMPRemoveTest::gmp_GetVideoDecoder, aNodeId, &decoder, &host), NS_DISPATCH_NORMAL); @@ -276,7 +276,7 @@ GMPRemoveTest::CreateVideoDecoder(nsCString aNodeId) nsTArray empty; mGMPThread->Dispatch( - NS_NewNonOwningRunnableMethodWithArgs&, GMPVideoDecoderCallbackProxy*, int32_t>( + NewNonOwningRunnableMethod&, GMPVideoDecoderCallbackProxy*, int32_t>( decoder, &GMPVideoDecoderProxy::InitDecode, codec, empty, this, 1 /* core count */), NS_DISPATCH_SYNC); @@ -328,7 +328,7 @@ void GMPRemoveTest::CloseVideoDecoder() { mGMPThread->Dispatch( - NS_NewNonOwningRunnableMethod(mDecoder, &GMPVideoDecoderProxy::Close), + NewNonOwningRunnableMethod(mDecoder, &GMPVideoDecoderProxy::Close), NS_DISPATCH_SYNC); mDecoder = nullptr; @@ -345,7 +345,7 @@ GMPErr GMPRemoveTest::Decode() { mGMPThread->Dispatch( - NS_NewNonOwningRunnableMethod(this, &GMPRemoveTest::gmp_Decode), + NewNonOwningRunnableMethod(this, &GMPRemoveTest::gmp_Decode), NS_DISPATCH_NORMAL); mTestMonitor.AwaitFinished(); diff --git a/dom/media/gtest/TestMP4Reader.cpp b/dom/media/gtest/TestMP4Reader.cpp index c0120c12a3..f08f7a40da 100644 --- a/dom/media/gtest/TestMP4Reader.cpp +++ b/dom/media/gtest/TestMP4Reader.cpp @@ -43,8 +43,8 @@ public: void Init() { nsCOMPtr thread; - nsresult rv = NS_NewThread(getter_AddRefs(thread), - NS_NewRunnableMethod(this, &TestBinding::ReadMetadata)); + nsCOMPtr r = NewRunnableMethod(this, &TestBinding::ReadMetadata); + nsresult rv = NS_NewThread(getter_AddRefs(thread), r); EXPECT_EQ(NS_OK, rv); thread->Shutdown(); } @@ -54,7 +54,7 @@ private: { { RefPtr queue = reader->OwnerThread(); - nsCOMPtr task = NS_NewRunnableMethod(reader, &MP4Reader::Shutdown); + nsCOMPtr task = NewRunnableMethod(reader, &MP4Reader::Shutdown); // Hackily bypass the tail dispatcher so that we can AwaitShutdownAndIdle. // In production code we'd use BeginShutdown + promises. queue->Dispatch(task.forget(), AbstractThread::AssertDispatchSuccess, diff --git a/dom/media/mediasink/DecodedAudioDataSink.cpp b/dom/media/mediasink/DecodedAudioDataSink.cpp index 89f94d200b..85a9520e00 100644 --- a/dom/media/mediasink/DecodedAudioDataSink.cpp +++ b/dom/media/mediasink/DecodedAudioDataSink.cpp @@ -51,6 +51,7 @@ DecodedAudioDataSink::DecodedAudioDataSink(AbstractThread* aThread, , mProcessedQueueLength(0) , mFramesParsed(0) , mLastEndTime(0) + , mIsAudioDataAudible(false) { bool resampling = gfxPrefs::AudioSinkResampling(); @@ -318,6 +319,7 @@ DecodedAudioDataSink::PopFrames(uint32_t aFrames) // We can now safely pop the audio packet from the processed queue. // This will fire the popped event, triggering a call to NotifyAudioNeeded. RefPtr releaseMe = mProcessedQueue.PopFront(); + CheckIsAudible(releaseMe); } return chunk; @@ -338,6 +340,18 @@ DecodedAudioDataSink::Drained() mEndPromise.ResolveIfExists(true, __func__); } +void +DecodedAudioDataSink::CheckIsAudible(const AudioData* aData) +{ + MOZ_ASSERT(aData); + + bool isAudible = aData->IsAudible(); + if (isAudible != mIsAudioDataAudible) { + mIsAudioDataAudible = isAudible; + mAudibleEvent.Notify(mIsAudioDataAudible); + } +} + void DecodedAudioDataSink::OnAudioPopped(const RefPtr& aSample) { diff --git a/dom/media/mediasink/DecodedAudioDataSink.h b/dom/media/mediasink/DecodedAudioDataSink.h index af6e935e08..36412984a3 100644 --- a/dom/media/mediasink/DecodedAudioDataSink.h +++ b/dom/media/mediasink/DecodedAudioDataSink.h @@ -58,6 +58,10 @@ public: void SetPreservesPitch(bool aPreservesPitch) override; void SetPlaying(bool aPlaying) override; + MediaEventSource& AudibleEvent() { + return mAudibleEvent; + } + private: virtual ~DecodedAudioDataSink(); @@ -70,6 +74,8 @@ private: bool Ended() const override; void Drained() override; + void CheckIsAudible(const AudioData* aData); + // The audio stream resource. Used on the task queue of MDSM only. RefPtr mAudioStream; @@ -146,6 +152,11 @@ private: // Never modifed after construction. uint32_t mOutputRate; uint32_t mOutputChannels; + + // True when audio is producing audible sound, false when audio is silent. + bool mIsAudioDataAudible; + + MediaEventProducer mAudibleEvent; }; } // namespace media diff --git a/dom/media/mediasink/DecodedStream.cpp b/dom/media/mediasink/DecodedStream.cpp index 1acd2779f8..ac82f074f0 100644 --- a/dom/media/mediasink/DecodedStream.cpp +++ b/dom/media/mediasink/DecodedStream.cpp @@ -54,7 +54,7 @@ public: { if (event == EVENT_FINISHED) { nsCOMPtr event = - NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished); + NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); } } @@ -99,9 +99,9 @@ UpdateStreamSuspended(MediaStream* aStream, bool aBlocking) } else { nsCOMPtr r; if (aBlocking) { - r = NS_NewRunnableMethod(aStream, &MediaStream::Suspend); + r = NewRunnableMethod(aStream, &MediaStream::Suspend); } else { - r = NS_NewRunnableMethod(aStream, &MediaStream::Resume); + r = NewRunnableMethod(aStream, &MediaStream::Resume); } AbstractThread::MainThread()->Dispatch(r.forget()); } diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index 715dc9858e..6ffb655277 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -177,7 +177,7 @@ void MediaSourceDemuxer::AttachSourceBuffer(TrackBuffersManager* aSourceBuffer) { nsCOMPtr task = - NS_NewRunnableMethodWithArg( + NewRunnableMethod( this, &MediaSourceDemuxer::DoAttachSourceBuffer, aSourceBuffer); GetTaskQueue()->Dispatch(task.forget()); @@ -195,7 +195,7 @@ void MediaSourceDemuxer::DetachSourceBuffer(TrackBuffersManager* aSourceBuffer) { nsCOMPtr task = - NS_NewRunnableMethodWithArg( + NewRunnableMethod( this, &MediaSourceDemuxer::DoDetachSourceBuffer, aSourceBuffer); GetTaskQueue()->Dispatch(task.forget()); diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index f28577097b..9d001721b7 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -173,9 +173,7 @@ TrackBuffersManager::ProcessTasks() NS_WARNING("Invalid Task"); } } - nsCOMPtr task = - NS_NewRunnableMethod(this, &TrackBuffersManager::ProcessTasks); - GetTaskQueue()->Dispatch(task.forget()); + GetTaskQueue()->Dispatch(NewRunnableMethod(this, &TrackBuffersManager::ProcessTasks)); } // A PromiseHolder will assert upon destruction if it has a pending promise @@ -768,9 +766,7 @@ TrackBuffersManager::ScheduleSegmentParserLoop() if (mDetached) { return; } - nsCOMPtr task = - NS_NewRunnableMethod(this, &TrackBuffersManager::SegmentParserLoop); - GetTaskQueue()->Dispatch(task.forget()); + GetTaskQueue()->Dispatch(NewRunnableMethod(this, &TrackBuffersManager::SegmentParserLoop)); } void @@ -956,11 +952,10 @@ TrackBuffersManager::OnDemuxerInitDone(nsresult) int64_t duration = std::max(videoDuration, audioDuration); // 1. Update the duration attribute if it currently equals NaN. // Those steps are performed by the MediaSourceDecoder::SetInitialDuration - nsCOMPtr task = - NS_NewRunnableMethodWithArg(mParentDecoder, - &MediaSourceDecoder::SetInitialDuration, - duration ? duration : -1); - AbstractThread::MainThread()->Dispatch(task.forget()); + AbstractThread::MainThread()->Dispatch(NewRunnableMethod + (mParentDecoder, + &MediaSourceDecoder::SetInitialDuration, + duration ? duration : -1)); // 2. If the initialization segment has no audio, video, or text tracks, then // run the append error algorithm with the decode error parameter set to true diff --git a/dom/media/mediasource/test/test_ResumeAfterClearing_mp4.html b/dom/media/mediasource/test/test_ResumeAfterClearing_mp4.html index 9b6622149c..b6769cb1b4 100644 --- a/dom/media/mediasource/test/test_ResumeAfterClearing_mp4.html +++ b/dom/media/mediasource/test/test_ResumeAfterClearing_mp4.html @@ -38,7 +38,7 @@ runWithMSE(function(ms, v) { }).then(function() { var promises = []; promises.push(once(v, "playing")); - promises.push(fetchAndLoad(sb, 'bipbop/bipbop', range(1,3), '.m4s')); + promises.push(fetchAndLoad(sb, 'bipbop/bipbop', range(1,4), '.m4s')); return Promise.all(promises); }).then(function() { ms.endOfStream(); diff --git a/dom/media/ogg/OggReader.cpp b/dom/media/ogg/OggReader.cpp index 3ffabd7c84..730329fc0d 100644 --- a/dom/media/ogg/OggReader.cpp +++ b/dom/media/ogg/OggReader.cpp @@ -169,17 +169,17 @@ nsresult OggReader::Init() { return NS_OK; } -nsresult OggReader::ResetDecode() +nsresult OggReader::ResetDecode(TargetQueues aQueues) { - return ResetDecode(false); + return ResetDecode(false, aQueues); } -nsresult OggReader::ResetDecode(bool start) +nsresult OggReader::ResetDecode(bool start, TargetQueues aQueues) { MOZ_ASSERT(OnTaskQueue()); nsresult res = NS_OK; - if (NS_FAILED(MediaDecoderReader::ResetDecode())) { + if (NS_FAILED(MediaDecoderReader::ResetDecode(aQueues))) { res = NS_ERROR_FAILURE; } diff --git a/dom/media/ogg/OggReader.h b/dom/media/ogg/OggReader.h index ba24aefde5..2c59940854 100644 --- a/dom/media/ogg/OggReader.h +++ b/dom/media/ogg/OggReader.h @@ -51,7 +51,7 @@ protected: public: nsresult Init() override; - nsresult ResetDecode() override; + nsresult ResetDecode(TargetQueues aQueues = AUDIO_VIDEO) override; bool DecodeAudioData() override; // If the Theora granulepos has not been captured, it may read several packets @@ -86,7 +86,7 @@ private: // Specialized Reset() method to signal if the seek is // to the start of the stream. - nsresult ResetDecode(bool start); + nsresult ResetDecode(bool start, TargetQueues aQueues = AUDIO_VIDEO); nsresult SeekInternal(int64_t aTime, int64_t aEndTime); @@ -94,7 +94,7 @@ private: return mSkeletonState != 0 && mSkeletonState->mActive; } - // Seeks to the keyframe preceeding the target time using available + // Seeks to the keyframe preceding the target time using available // keyframe indexes. enum IndexedSeekResult { SEEK_OK, // Success. diff --git a/dom/media/omx/AudioOffloadPlayer.cpp b/dom/media/omx/AudioOffloadPlayer.cpp index 05e5a45e67..ccdd82ba84 100644 --- a/dom/media/omx/AudioOffloadPlayer.cpp +++ b/dom/media/omx/AudioOffloadPlayer.cpp @@ -354,7 +354,7 @@ status_t AudioOffloadPlayer::DoSeek() if (!mSeekPromise.IsEmpty()) { nsCOMPtr nsEvent = - NS_NewRunnableMethodWithArg( + NewRunnableMethod( mObserver, &MediaDecoder::SeekingStarted, mSeekTarget.mEventVisibility); @@ -425,16 +425,14 @@ void AudioOffloadPlayer::NotifyAudioEOS() MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility); mSeekPromise.Resolve(val, __func__); } - nsCOMPtr nsEvent = NS_NewRunnableMethod(mObserver, - &MediaDecoder::PlaybackEnded); - NS_DispatchToMainThread(nsEvent); + NS_DispatchToMainThread(NewRunnableMethod(mObserver, + &MediaDecoder::PlaybackEnded)); } void AudioOffloadPlayer::NotifyPositionChanged() { - nsCOMPtr nsEvent = - NS_NewRunnableMethod(mObserver, &MediaOmxCommonDecoder::NotifyOffloadPlayerPositionChanged); - NS_DispatchToMainThread(nsEvent); + NS_DispatchToMainThread(NewRunnableMethod(mObserver, + &MediaOmxCommonDecoder::NotifyOffloadPlayerPositionChanged)); } void AudioOffloadPlayer::NotifyAudioTearDown() @@ -448,9 +446,8 @@ void AudioOffloadPlayer::NotifyAudioTearDown() MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility); mSeekPromise.Resolve(val, __func__); } - nsCOMPtr nsEvent = NS_NewRunnableMethod(mObserver, - &MediaOmxCommonDecoder::AudioOffloadTearDown); - NS_DispatchToMainThread(nsEvent); + NS_DispatchToMainThread(NewRunnableMethod(mObserver, + &MediaOmxCommonDecoder::AudioOffloadTearDown)); } // static diff --git a/dom/media/omx/MediaOmxCommonDecoder.cpp b/dom/media/omx/MediaOmxCommonDecoder.cpp index 613fe02682..11eef8a00c 100644 --- a/dom/media/omx/MediaOmxCommonDecoder.cpp +++ b/dom/media/omx/MediaOmxCommonDecoder.cpp @@ -252,6 +252,7 @@ void MediaOmxCommonDecoder::SetElementVisibility(bool aIsVisible) { MOZ_ASSERT(NS_IsMainThread()); + MediaDecoder::SetElementVisibility(aIsVisible); if (mAudioOffloadPlayer) { mAudioOffloadPlayer->SetElementVisibility(aIsVisible); } diff --git a/dom/media/platforms/agnostic/OpusDecoder.cpp b/dom/media/platforms/agnostic/OpusDecoder.cpp index e2cbba3139..9fa5e91f55 100644 --- a/dom/media/platforms/agnostic/OpusDecoder.cpp +++ b/dom/media/platforms/agnostic/OpusDecoder.cpp @@ -10,6 +10,7 @@ #include "VorbisDecoder.h" // For VorbisLayout #include "mozilla/Endian.h" #include "mozilla/PodOperations.h" +#include "mozilla/SyncRunnable.h" #include #include // For PRId64 @@ -21,7 +22,7 @@ extern mozilla::LogModule* GetPDMLog(); namespace mozilla { OpusDataDecoder::OpusDataDecoder(const AudioInfo& aConfig, - FlushableTaskQueue* aTaskQueue, + TaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback) : mInfo(aConfig) , mTaskQueue(aTaskQueue) @@ -31,6 +32,7 @@ OpusDataDecoder::OpusDataDecoder(const AudioInfo& aConfig, , mDecodedHeader(false) , mPaddingDiscarded(false) , mFrames(0) + , mIsFlushing(false) { } @@ -132,18 +134,18 @@ OpusDataDecoder::DecodeHeader(const unsigned char* aData, size_t aLength) nsresult OpusDataDecoder::Input(MediaRawData* aSample) { - nsCOMPtr runnable( - NS_NewRunnableMethodWithArg>( - this, &OpusDataDecoder::Decode, - RefPtr(aSample))); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod>( + this, &OpusDataDecoder::ProcessDecode, aSample)); return NS_OK; } void -OpusDataDecoder::Decode(MediaRawData* aSample) +OpusDataDecoder::ProcessDecode(MediaRawData* aSample) { + if (mIsFlushing) { + return; + } if (DoDecode(aSample) == -1) { mCallback->Error(); } else if(mTaskQueue->IsEmpty()) { @@ -301,7 +303,7 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample) } void -OpusDataDecoder::DoDrain() +OpusDataDecoder::ProcessDrain() { mCallback->DrainComplete(); } @@ -309,23 +311,27 @@ OpusDataDecoder::DoDrain() nsresult OpusDataDecoder::Drain() { - RefPtr runnable( - NS_NewRunnableMethod(this, &OpusDataDecoder::DoDrain)); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &OpusDataDecoder::ProcessDrain)); return NS_OK; } nsresult OpusDataDecoder::Flush() { - mTaskQueue->Flush(); - if (mOpusDecoder) { + if (!mOpusDecoder) { + return NS_OK; + } + mIsFlushing = true; + nsCOMPtr runnable = NS_NewRunnableFunction([this] () { + MOZ_ASSERT(mOpusDecoder); // Reset the decoder. opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE); mSkip = mOpusParser->mPreSkip; mPaddingDiscarded = false; mLastFrameTime.reset(); - } + }); + SyncRunnable::DispatchToThread(mTaskQueue, runnable); + mIsFlushing = false; return NS_OK; } diff --git a/dom/media/platforms/agnostic/OpusDecoder.h b/dom/media/platforms/agnostic/OpusDecoder.h index 88df529697..13572471cb 100644 --- a/dom/media/platforms/agnostic/OpusDecoder.h +++ b/dom/media/platforms/agnostic/OpusDecoder.h @@ -18,7 +18,7 @@ class OpusDataDecoder : public MediaDataDecoder { public: OpusDataDecoder(const AudioInfo& aConfig, - FlushableTaskQueue* aTaskQueue, + TaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback); ~OpusDataDecoder(); @@ -38,12 +38,12 @@ public: private: nsresult DecodeHeader(const unsigned char* aData, size_t aLength); - void Decode (MediaRawData* aSample); - int DoDecode (MediaRawData* aSample); - void DoDrain (); + void ProcessDecode(MediaRawData* aSample); + int DoDecode(MediaRawData* aSample); + void ProcessDrain(); const AudioInfo& mInfo; - RefPtr mTaskQueue; + const RefPtr mTaskQueue; MediaDataDecoderCallback* mCallback; // Opus decoder state @@ -60,6 +60,8 @@ private: int64_t mFrames; Maybe mLastFrameTime; uint8_t mMappingTable[MAX_AUDIO_CHANNELS]; // Channel mapping table. + + Atomic mIsFlushing; }; } // namespace mozilla diff --git a/dom/media/platforms/agnostic/VPXDecoder.cpp b/dom/media/platforms/agnostic/VPXDecoder.cpp index 644361ecaa..6b1f5ad2ce 100644 --- a/dom/media/platforms/agnostic/VPXDecoder.cpp +++ b/dom/media/platforms/agnostic/VPXDecoder.cpp @@ -186,11 +186,9 @@ VPXDecoder::DecodeFrame(MediaRawData* aSample) nsresult VPXDecoder::Input(MediaRawData* aSample) { - nsCOMPtr runnable( - NS_NewRunnableMethodWithArg>( - this, &VPXDecoder::DecodeFrame, - RefPtr(aSample))); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod>( + this, &VPXDecoder::DecodeFrame, + RefPtr(aSample))); return NS_OK; } @@ -204,9 +202,7 @@ VPXDecoder::DoDrain() nsresult VPXDecoder::Drain() { - nsCOMPtr runnable( - NS_NewRunnableMethod(this, &VPXDecoder::DoDrain)); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &VPXDecoder::DoDrain)); return NS_OK; } diff --git a/dom/media/platforms/agnostic/VorbisDecoder.cpp b/dom/media/platforms/agnostic/VorbisDecoder.cpp index 259df44d78..ed7217c457 100644 --- a/dom/media/platforms/agnostic/VorbisDecoder.cpp +++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp @@ -129,11 +129,9 @@ VorbisDataDecoder::DecodeHeader(const unsigned char* aData, size_t aLength) nsresult VorbisDataDecoder::Input(MediaRawData* aSample) { - nsCOMPtr runnable( - NS_NewRunnableMethodWithArg>( - this, &VorbisDataDecoder::Decode, - RefPtr(aSample))); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod>( + this, &VorbisDataDecoder::Decode, + RefPtr(aSample))); return NS_OK; } @@ -265,9 +263,7 @@ VorbisDataDecoder::DoDrain() nsresult VorbisDataDecoder::Drain() { - nsCOMPtr runnable( - NS_NewRunnableMethod(this, &VorbisDataDecoder::DoDrain)); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &VorbisDataDecoder::DoDrain)); return NS_OK; } diff --git a/dom/media/platforms/agnostic/WAVDecoder.cpp b/dom/media/platforms/agnostic/WAVDecoder.cpp index 677e68b436..d31d514379 100644 --- a/dom/media/platforms/agnostic/WAVDecoder.cpp +++ b/dom/media/platforms/agnostic/WAVDecoder.cpp @@ -70,11 +70,9 @@ WaveDataDecoder::Init() nsresult WaveDataDecoder::Input(MediaRawData* aSample) { - nsCOMPtr runnable( - NS_NewRunnableMethodWithArg>( - this, &WaveDataDecoder::Decode, - RefPtr(aSample))); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod>( + this, &WaveDataDecoder::Decode, + RefPtr(aSample))); return NS_OK; } @@ -158,9 +156,7 @@ WaveDataDecoder::DoDrain() nsresult WaveDataDecoder::Drain() { - nsCOMPtr runnable( - NS_NewRunnableMethod(this, &WaveDataDecoder::DoDrain)); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &WaveDataDecoder::DoDrain)); return NS_OK; } @@ -186,4 +182,4 @@ WaveDataDecoder::IsWave(const nsACString& aMimeType) } } // namespace mozilla -#undef LOG \ No newline at end of file +#undef LOG diff --git a/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp b/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp index 8808ff23b9..503de5380f 100644 --- a/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp +++ b/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp @@ -58,9 +58,7 @@ MediaDataDecoderProxy::Flush() mFlushComplete.Set(false); - nsCOMPtr task; - task = NS_NewRunnableMethod(mProxyDecoder, &MediaDataDecoder::Flush); - mProxyThread->Dispatch(task.forget()); + mProxyThread->Dispatch(NewRunnableMethod(mProxyDecoder, &MediaDataDecoder::Flush)); mFlushComplete.WaitUntil(true); @@ -73,9 +71,7 @@ MediaDataDecoderProxy::Drain() MOZ_ASSERT(!IsOnProxyThread()); MOZ_ASSERT(!mIsShutdown); - nsCOMPtr task; - task = NS_NewRunnableMethod(mProxyDecoder, &MediaDataDecoder::Drain); - mProxyThread->Dispatch(task.forget()); + mProxyThread->Dispatch(NewRunnableMethod(mProxyDecoder, &MediaDataDecoder::Drain)); return NS_OK; } @@ -87,9 +83,9 @@ MediaDataDecoderProxy::Shutdown() #if defined(DEBUG) mIsShutdown = true; #endif - nsCOMPtr task; - task = NS_NewRunnableMethod(mProxyDecoder, &MediaDataDecoder::Shutdown); - nsresult rv = mProxyThread->AsXPCOMThread()->Dispatch(task, NS_DISPATCH_SYNC); + nsresult rv = mProxyThread->AsXPCOMThread()->Dispatch(NewRunnableMethod(mProxyDecoder, + &MediaDataDecoder::Shutdown), + NS_DISPATCH_SYNC); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } diff --git a/dom/media/platforms/android/AndroidDecoderModule.cpp b/dom/media/platforms/android/AndroidDecoderModule.cpp index cc09d2c228..f878f49354 100644 --- a/dom/media/platforms/android/AndroidDecoderModule.cpp +++ b/dom/media/platforms/android/AndroidDecoderModule.cpp @@ -389,9 +389,8 @@ MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface) NS_ENSURE_SUCCESS(rv = ResetInputBuffers(), rv); NS_ENSURE_SUCCESS(rv = ResetOutputBuffers(), rv); - rv = NS_NewNamedThread( - "MC Decoder", getter_AddRefs(mThread), - NS_NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop)); + nsCOMPtr r = NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop); + rv = NS_NewNamedThread("MC Decoder", getter_AddRefs(mThread), r); return rv; } diff --git a/dom/media/platforms/apple/AppleATDecoder.cpp b/dom/media/platforms/apple/AppleATDecoder.cpp index 6d364c25d9..a5da492aa1 100644 --- a/dom/media/platforms/apple/AppleATDecoder.cpp +++ b/dom/media/platforms/apple/AppleATDecoder.cpp @@ -74,7 +74,7 @@ AppleATDecoder::Input(MediaRawData* aSample) // Queue a task to perform the actual decoding on a separate thread. nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( this, &AppleATDecoder::SubmitSample, RefPtr(aSample)); diff --git a/dom/media/platforms/apple/AppleVDADecoder.cpp b/dom/media/platforms/apple/AppleVDADecoder.cpp index 89b5e66c8d..2c96100401 100644 --- a/dom/media/platforms/apple/AppleVDADecoder.cpp +++ b/dom/media/platforms/apple/AppleVDADecoder.cpp @@ -100,7 +100,7 @@ AppleVDADecoder::Shutdown() mIsShutDown = true; if (mTaskQueue) { nsCOMPtr runnable = - NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessShutdown); + NewRunnableMethod(this, &AppleVDADecoder::ProcessShutdown); mTaskQueue->Dispatch(runnable.forget()); } else { ProcessShutdown(); @@ -133,7 +133,7 @@ AppleVDADecoder::Input(MediaRawData* aSample) mInputIncoming++; nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( this, &AppleVDADecoder::SubmitFrame, RefPtr(aSample)); @@ -148,7 +148,7 @@ AppleVDADecoder::Flush() mIsFlushing = true; mTaskQueue->Flush(); nsCOMPtr runnable = - NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessFlush); + NewRunnableMethod(this, &AppleVDADecoder::ProcessFlush); MonitorAutoLock mon(mMonitor); mTaskQueue->Dispatch(runnable.forget()); while (mIsFlushing) { @@ -163,7 +163,7 @@ AppleVDADecoder::Drain() { MOZ_ASSERT(mCallback->OnReaderTaskQueue()); nsCOMPtr runnable = - NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessDrain); + NewRunnableMethod(this, &AppleVDADecoder::ProcessDrain); mTaskQueue->Dispatch(runnable.forget()); return NS_OK; } diff --git a/dom/media/platforms/apple/AppleVTDecoder.cpp b/dom/media/platforms/apple/AppleVTDecoder.cpp index 000f95c638..d3e444688c 100644 --- a/dom/media/platforms/apple/AppleVTDecoder.cpp +++ b/dom/media/platforms/apple/AppleVTDecoder.cpp @@ -107,7 +107,7 @@ AppleVTDecoder::Input(MediaRawData* aSample) mInputIncoming++; nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( this, &AppleVTDecoder::SubmitFrame, aSample); mTaskQueue->Dispatch(runnable.forget()); return NS_OK; diff --git a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp index eb8698b714..8fcacb9350 100644 --- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp @@ -175,7 +175,7 @@ FFmpegAudioDecoder::DecodePacket(MediaRawData* aSample) nsresult FFmpegAudioDecoder::Input(MediaRawData* aSample) { - nsCOMPtr runnable(NS_NewRunnableMethodWithArg>( + nsCOMPtr runnable(NewRunnableMethod>( this, &FFmpegAudioDecoder::DecodePacket, RefPtr(aSample))); mTaskQueue->Dispatch(runnable.forget()); return NS_OK; diff --git a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp index e3199fccf0..cdedb25794 100644 --- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp @@ -110,7 +110,7 @@ FFmpegDataDecoder::Shutdown() { if (mTaskQueue) { nsCOMPtr runnable = - NS_NewRunnableMethod(this, &FFmpegDataDecoder::ProcessShutdown); + NewRunnableMethod(this, &FFmpegDataDecoder::ProcessShutdown); mTaskQueue->Dispatch(runnable.forget()); } else { ProcessShutdown(); @@ -125,7 +125,7 @@ FFmpegDataDecoder::Flush() mIsFlushing = true; mTaskQueue->Flush(); nsCOMPtr runnable = - NS_NewRunnableMethod(this, &FFmpegDataDecoder::ProcessFlush); + NewRunnableMethod(this, &FFmpegDataDecoder::ProcessFlush); MonitorAutoLock mon(mMonitor); mTaskQueue->Dispatch(runnable.forget()); while (mIsFlushing) { @@ -139,7 +139,7 @@ FFmpegDataDecoder::Drain() { MOZ_ASSERT(mCallback->OnReaderTaskQueue()); nsCOMPtr runnable = - NS_NewRunnableMethod(this, &FFmpegDataDecoder::ProcessDrain); + NewRunnableMethod(this, &FFmpegDataDecoder::ProcessDrain); mTaskQueue->Dispatch(runnable.forget()); return NS_OK; } diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp index 0c7baf2c6c..06a3462458 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp @@ -339,7 +339,7 @@ nsresult FFmpegVideoDecoder::Input(MediaRawData* aSample) { nsCOMPtr runnable( - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( this, &FFmpegVideoDecoder::DecodeFrame, RefPtr(aSample))); mTaskQueue->Dispatch(runnable.forget()); diff --git a/dom/media/platforms/omx/OmxDataDecoder.cpp b/dom/media/platforms/omx/OmxDataDecoder.cpp index 2f90ff5fd2..59768b00ac 100644 --- a/dom/media/platforms/omx/OmxDataDecoder.cpp +++ b/dom/media/platforms/omx/OmxDataDecoder.cpp @@ -114,9 +114,7 @@ OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo, LOG(""); mOmxLayer = new OmxPromiseLayer(mOmxTaskQueue, this, aImageContainer); - nsCOMPtr r = - NS_NewRunnableMethod(this, &OmxDataDecoder::InitializationTask); - mOmxTaskQueue->Dispatch(r.forget()); + mOmxTaskQueue->Dispatch(NewRunnableMethod(this, &OmxDataDecoder::InitializationTask)); } OmxDataDecoder::~OmxDataDecoder() @@ -209,9 +207,7 @@ OmxDataDecoder::Flush() mFlushing = true; - nsCOMPtr r = - NS_NewRunnableMethod(this, &OmxDataDecoder::DoFlush); - mOmxTaskQueue->Dispatch(r.forget()); + mOmxTaskQueue->Dispatch(NewRunnableMethod(this, &OmxDataDecoder::DoFlush)); // According to the definition of Flush() in PDM: // "the decoder must be ready to accept new input for decoding". @@ -229,9 +225,7 @@ OmxDataDecoder::Drain() { LOG(""); - nsCOMPtr r = - NS_NewRunnableMethod(this, &OmxDataDecoder::SendEosBuffer); - mOmxTaskQueue->Dispatch(r.forget()); + mOmxTaskQueue->Dispatch(NewRunnableMethod(this, &OmxDataDecoder::SendEosBuffer)); return NS_OK; } @@ -243,9 +237,7 @@ OmxDataDecoder::Shutdown() mShuttingDown = true; - nsCOMPtr r = - NS_NewRunnableMethod(this, &OmxDataDecoder::DoAsyncShutdown); - mOmxTaskQueue->Dispatch(r.forget()); + mOmxTaskQueue->Dispatch(NewRunnableMethod(this, &OmxDataDecoder::DoAsyncShutdown)); { // DoAsyncShutdown() will be running for a while, it could be still running diff --git a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp index db263df98c..a947d110a2 100644 --- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp @@ -45,9 +45,7 @@ WMFMediaDataDecoder::Shutdown() MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown); if (mTaskQueue) { - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown)); } else { ProcessShutdown(); } @@ -75,7 +73,7 @@ WMFMediaDataDecoder::Input(MediaRawData* aSample) MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown); nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( this, &WMFMediaDataDecoder::ProcessDecode, RefPtr(aSample)); @@ -151,11 +149,9 @@ WMFMediaDataDecoder::Flush() MOZ_ASSERT(mCallback->OnReaderTaskQueue()); MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown); - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessFlush); MonitorAutoLock mon(mMonitor); mIsFlushing = true; - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessFlush)); while (mIsFlushing) { mon.Wait(); } @@ -185,9 +181,7 @@ WMFMediaDataDecoder::Drain() MOZ_ASSERT(mCallback->OnReaderTaskQueue()); MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown); - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain); - mTaskQueue->Dispatch(runnable.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain)); return NS_OK; } @@ -204,7 +198,7 @@ WMFMediaDataDecoder::ConfigurationChanged(const TrackInfo& aConfig) MOZ_ASSERT(mCallback->OnReaderTaskQueue()); nsCOMPtr runnable = - NS_NewRunnableMethodWithArg&&>( + NewRunnableMethod&&>( this, &WMFMediaDataDecoder::ProcessConfigurationChanged, aConfig.Clone()); diff --git a/dom/media/platforms/wrappers/FuzzingWrapper.cpp b/dom/media/platforms/wrappers/FuzzingWrapper.cpp index 0cb776c582..3e4081f19d 100644 --- a/dom/media/platforms/wrappers/FuzzingWrapper.cpp +++ b/dom/media/platforms/wrappers/FuzzingWrapper.cpp @@ -136,7 +136,7 @@ DecoderCallbackFuzzingWrapper::Output(MediaData* aData) { if (!mTaskQueue->IsCurrentThreadIn()) { nsCOMPtr task = - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( this, &DecoderCallbackFuzzingWrapper::Output, aData); mTaskQueue->Dispatch(task.forget()); return; @@ -176,9 +176,7 @@ void DecoderCallbackFuzzingWrapper::Error() { if (!mTaskQueue->IsCurrentThreadIn()) { - nsCOMPtr task = - NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::Error); - mTaskQueue->Dispatch(task.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::Error)); return; } CFW_LOGV(""); @@ -191,9 +189,7 @@ void DecoderCallbackFuzzingWrapper::InputExhausted() { if (!mTaskQueue->IsCurrentThreadIn()) { - nsCOMPtr task = - NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::InputExhausted); - mTaskQueue->Dispatch(task.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::InputExhausted)); return; } if (!mDontDelayInputExhausted && !mDelayedOutput.empty()) { @@ -212,9 +208,7 @@ void DecoderCallbackFuzzingWrapper::DrainComplete() { if (!mTaskQueue->IsCurrentThreadIn()) { - nsCOMPtr task = - NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::DrainComplete); - mTaskQueue->Dispatch(task.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::DrainComplete)); return; } MOZ_ASSERT(mCallback); @@ -233,9 +227,7 @@ void DecoderCallbackFuzzingWrapper::ReleaseMediaResources() { if (!mTaskQueue->IsCurrentThreadIn()) { - nsCOMPtr task = - NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ReleaseMediaResources); - mTaskQueue->Dispatch(task.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ReleaseMediaResources)); return; } CFW_LOGV(""); @@ -317,9 +309,7 @@ DecoderCallbackFuzzingWrapper::ClearDelayedOutput() { if (!mTaskQueue->IsCurrentThreadIn()) { DFW_LOGV("(dispatching self)"); - nsCOMPtr task = - NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ClearDelayedOutput); - mTaskQueue->Dispatch(task.forget()); + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ClearDelayedOutput)); return; } DFW_LOGV(""); diff --git a/dom/media/raw/RawReader.cpp b/dom/media/raw/RawReader.cpp index aa562d53d9..3b1a0c0527 100644 --- a/dom/media/raw/RawReader.cpp +++ b/dom/media/raw/RawReader.cpp @@ -27,10 +27,10 @@ RawReader::~RawReader() MOZ_COUNT_DTOR(RawReader); } -nsresult RawReader::ResetDecode() +nsresult RawReader::ResetDecode(TargetQueues aQueues) { mCurrentFrame = 0; - return MediaDecoderReader::ResetDecode(); + return MediaDecoderReader::ResetDecode(aQueues); } nsresult RawReader::ReadMetadata(MediaInfo* aInfo, diff --git a/dom/media/raw/RawReader.h b/dom/media/raw/RawReader.h index d73d22fc32..0c7696893c 100644 --- a/dom/media/raw/RawReader.h +++ b/dom/media/raw/RawReader.h @@ -20,7 +20,7 @@ protected: ~RawReader(); public: - nsresult ResetDecode() override; + nsresult ResetDecode(TargetQueues aQueues) override; bool DecodeAudioData() override; bool DecodeVideoFrame(bool &aKeyframeSkip, diff --git a/dom/media/systemservices/MediaSystemResourceManager.cpp b/dom/media/systemservices/MediaSystemResourceManager.cpp index 4d833dd239..88dcc7080e 100644 --- a/dom/media/systemservices/MediaSystemResourceManager.cpp +++ b/dom/media/systemservices/MediaSystemResourceManager.cpp @@ -199,7 +199,7 @@ MediaSystemResourceManager::Acquire(MediaSystemResourceClient* aClient) } aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_WAITING; ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask( - NewRunnableMethod( + NewRunnableMethod( this, &MediaSystemResourceManager::DoAcquire, aClient->mId)); @@ -242,7 +242,7 @@ MediaSystemResourceManager::AcquireSyncNoWait(MediaSystemResourceClient* aClient } ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask( - NewRunnableMethod( + NewRunnableMethod( this, &MediaSystemResourceManager::DoAcquire, aClient->mId)); @@ -309,7 +309,7 @@ MediaSystemResourceManager::ReleaseResource(MediaSystemResourceClient* aClient) aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_END; ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask( - NewRunnableMethod( + NewRunnableMethod( this, &MediaSystemResourceManager::DoRelease, aClient->mId)); @@ -337,7 +337,7 @@ MediaSystemResourceManager::HandleAcquireResult(uint32_t aId, bool aSuccess) { if (!InImageBridgeChildThread()) { ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask( - NewRunnableMethod( + NewRunnableMethod( this, &MediaSystemResourceManager::HandleAcquireResult, aId, diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp index f5f25e7c15..6e410cba52 100644 --- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -30,6 +30,7 @@ #include "DelayNode.h" #include "DynamicsCompressorNode.h" #include "GainNode.h" +#include "IIRFilterNode.h" #include "MediaElementAudioSourceNode.h" #include "MediaStreamAudioDestinationNode.h" #include "MediaStreamAudioSourceNode.h" @@ -503,6 +504,42 @@ AudioContext::CreateBiquadFilter(ErrorResult& aRv) return filterNode.forget(); } +already_AddRefed +AudioContext::CreateIIRFilter(const mozilla::dom::binding_detail::AutoSequence& aFeedforward, + const mozilla::dom::binding_detail::AutoSequence& aFeedback, + mozilla::ErrorResult& aRv) +{ + if (CheckClosed(aRv)) { + return nullptr; + } + + if (aFeedforward.Length() == 0 || aFeedforward.Length() > 20) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + + if (aFeedback.Length() == 0 || aFeedback.Length() > 20) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + + bool feedforwardAllZeros = true; + for (size_t i = 0; i < aFeedforward.Length(); ++i) { + if (aFeedforward.Elements()[i] != 0.0) { + feedforwardAllZeros = false; + } + } + + if (feedforwardAllZeros || aFeedback.Elements()[0] == 0.0) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + + RefPtr filterNode = + new IIRFilterNode(this, aFeedforward, aFeedback); + return filterNode.forget(); +} + already_AddRefed AudioContext::CreateOscillator(ErrorResult& aRv) { diff --git a/dom/media/webaudio/AudioContext.h b/dom/media/webaudio/AudioContext.h index 1a953cf355..a5ddbde182 100644 --- a/dom/media/webaudio/AudioContext.h +++ b/dom/media/webaudio/AudioContext.h @@ -57,9 +57,10 @@ class ConvolverNode; class DelayNode; class DynamicsCompressorNode; class GainNode; -class HTMLMediaElement; -class MediaElementAudioSourceNode; class GlobalObject; +class HTMLMediaElement; +class IIRFilterNode; +class MediaElementAudioSourceNode; class MediaStreamAudioDestinationNode; class MediaStreamAudioSourceNode; class OscillatorNode; @@ -251,6 +252,11 @@ public: already_AddRefed CreateBiquadFilter(ErrorResult& aRv); + already_AddRefed + CreateIIRFilter(const mozilla::dom::binding_detail::AutoSequence& aFeedforward, + const mozilla::dom::binding_detail::AutoSequence& aFeedback, + mozilla::ErrorResult& aRv); + already_AddRefed CreateOscillator(ErrorResult& aRv); diff --git a/dom/media/webaudio/AudioDestinationNode.cpp b/dom/media/webaudio/AudioDestinationNode.cpp index fdf1108038..55ccba817d 100644 --- a/dom/media/webaudio/AudioDestinationNode.cpp +++ b/dom/media/webaudio/AudioDestinationNode.cpp @@ -305,11 +305,6 @@ private: bool mSuspended; }; -static bool UseAudioChannelAPI() -{ - return Preferences::GetBool("media.useAudioChannelAPI"); -} - NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode, mAudioChannelAgent, mOfflineRenderingPromise) @@ -331,7 +326,7 @@ AudioDestinationNode::AudioDestinationNode(AudioContext* aContext, , mFramesToProduce(aLength) , mAudioChannel(AudioChannel::Normal) , mIsOffline(aIsOffline) - , mAudioChannelAgentPlaying(false) + , mAudioChannelSuspended(false) , mCaptured(false) { MediaStreamGraph* graph = aIsOffline ? @@ -410,9 +405,8 @@ AudioDestinationNode::NotifyMainThreadStreamFinished() MOZ_ASSERT(mStream->IsFinished()); if (mIsOffline) { - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &AudioDestinationNode::FireOfflineCompletionEvent); - NS_DispatchToCurrentThread(runnable); + NS_DispatchToCurrentThread(NewRunnableMethod(this, + &AudioDestinationNode::FireOfflineCompletionEvent)); } } @@ -501,31 +495,36 @@ AudioDestinationNode::StartRendering(Promise* aPromise) mStream->Graph()->StartNonRealtimeProcessing(mFramesToProduce); } -void -AudioDestinationNode::SetCanPlay(float aVolume, bool aMuted) -{ - if (!mStream) { - return; - } - - mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !aMuted); - mStream->SetAudioOutputVolume(&gWebAudioOutputKey, aVolume); -} - NS_IMETHODIMP AudioDestinationNode::WindowVolumeChanged(float aVolume, bool aMuted) { - if (aMuted != mAudioChannelAgentPlaying) { - mAudioChannelAgentPlaying = aMuted; - - if (UseAudioChannelAPI()) { - Context()->DispatchTrustedEvent( - !aMuted ? NS_LITERAL_STRING("mozinterruptend") - : NS_LITERAL_STRING("mozinterruptbegin")); - } + if (!mStream) { + return NS_OK; } - SetCanPlay(aVolume, aMuted); + float volume = aMuted ? 0.0 : aVolume; + mStream->SetAudioOutputVolume(&gWebAudioOutputKey, volume); + return NS_OK; +} + +NS_IMETHODIMP +AudioDestinationNode::WindowSuspendChanged(nsSuspendedTypes aSuspend) +{ + if (!mStream) { + return NS_OK; + } + + bool suspended = (aSuspend != nsISuspendedTypes::NONE_SUSPENDED); + if (mAudioChannelSuspended == suspended) { + return NS_OK; + } + + mAudioChannelSuspended = suspended; + Context()->DispatchTrustedEvent(!suspended ? + NS_LITERAL_STRING("mozinterruptend") : + NS_LITERAL_STRING("mozinterruptbegin")); + + mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !suspended); return NS_OK; } @@ -664,14 +663,15 @@ AudioDestinationNode::InputMuted(bool aMuted) return; } - float volume = 0.0; - bool muted = true; - nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted); + AudioPlaybackConfig config; + nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config, + AudioChannelService::AudibleState::eAudible); if (NS_WARN_IF(NS_FAILED(rv))) { return; } - WindowVolumeChanged(volume, muted); + WindowVolumeChanged(config.mVolume, config.mMuted); + WindowSuspendChanged(config.mSuspend); } } // namespace dom diff --git a/dom/media/webaudio/AudioDestinationNode.h b/dom/media/webaudio/AudioDestinationNode.h index 708cd1cc6e..cf0db78627 100644 --- a/dom/media/webaudio/AudioDestinationNode.h +++ b/dom/media/webaudio/AudioDestinationNode.h @@ -92,8 +92,6 @@ private: void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv); bool CheckAudioChannelPermissions(AudioChannel aValue); - void SetCanPlay(float aVolume, bool aMuted); - SelfReference mOfflineRenderingRef; uint32_t mFramesToProduce; @@ -105,7 +103,7 @@ private: // Audio Channel Type. AudioChannel mAudioChannel; bool mIsOffline; - bool mAudioChannelAgentPlaying; + bool mAudioChannelSuspended; bool mCaptured; }; diff --git a/dom/media/webaudio/IIRFilterNode.cpp b/dom/media/webaudio/IIRFilterNode.cpp new file mode 100644 index 0000000000..c39b5d33e3 --- /dev/null +++ b/dom/media/webaudio/IIRFilterNode.cpp @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "IIRFilterNode.h" +#include "AudioNodeEngine.h" + +#include "blink/IIRFilter.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_ISUPPORTS_INHERITED0(IIRFilterNode, AudioNode) + +class IIRFilterNodeEngine final : public AudioNodeEngine +{ +public: + IIRFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination, + const AudioDoubleArray &aFeedforward, + const AudioDoubleArray &aFeedback, + uint64_t aWindowID) + : AudioNodeEngine(aNode) + , mDestination(aDestination->Stream()) + , mFeedforward(aFeedforward) + , mFeedback(aFeedback) + , mWindowID(aWindowID) + { + } + + void ProcessBlock(AudioNodeStream* aStream, + GraphTime aFrom, + const AudioBlock& aInput, + AudioBlock* aOutput, + bool* aFinished) override + { + float inputBuffer[WEBAUDIO_BLOCK_SIZE + 4]; + float* alignedInputBuffer = ALIGNED16(inputBuffer); + ASSERT_ALIGNED16(alignedInputBuffer); + + if (aInput.IsNull()) { + if (!mIIRFilters.IsEmpty()) { + bool allZero = true; + for (uint32_t i = 0; i < mIIRFilters.Length(); ++i) { + allZero &= mIIRFilters[i]->buffersAreZero(); + } + + // all filter buffer values are zero, so the output will be zero + // as well. + if (allZero) { + mIIRFilters.Clear(); + aStream->ScheduleCheckForInactive(); + + RefPtr refchanged = + new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE); + aStream->Graph()-> + DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget()); + + aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); + return; + } + + PodZero(alignedInputBuffer, WEBAUDIO_BLOCK_SIZE); + } + } else if(mIIRFilters.Length() != aInput.ChannelCount()){ + if (mIIRFilters.IsEmpty()) { + RefPtr refchanged = + new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF); + aStream->Graph()-> + DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget()); + } else { + WebAudioUtils::LogToDeveloperConsole(mWindowID, + "IIRFilterChannelCountChangeWarning"); + } + + // Adjust the number of filters based on the number of channels + mIIRFilters.SetLength(aInput.ChannelCount()); + for (size_t i = 0; i < aInput.ChannelCount(); ++i) { + mIIRFilters[i] = new blink::IIRFilter(&mFeedforward, &mFeedback); + } + } + + uint32_t numberOfChannels = mIIRFilters.Length(); + aOutput->AllocateChannels(numberOfChannels); + + for (uint32_t i = 0; i < numberOfChannels; ++i) { + const float* input; + if (aInput.IsNull()) { + input = alignedInputBuffer; + } else { + input = static_cast(aInput.mChannelData[i]); + if (aInput.mVolume != 1.0) { + AudioBlockCopyChannelWithScale(input, aInput.mVolume, alignedInputBuffer); + input = alignedInputBuffer; + } + } + + mIIRFilters[i]->process(input, + aOutput->ChannelFloatsForWrite(i), + aInput.GetDuration()); + } + } + + bool IsActive() const override + { + return !mIIRFilters.IsEmpty(); + } + + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override + { + // Not owned: + // - mDestination - probably not owned + // - AudioParamTimelines - counted in the AudioNode + size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); + amount += mIIRFilters.ShallowSizeOfExcludingThis(aMallocSizeOf); + return amount; + } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override + { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); + } + +private: + AudioNodeStream* mDestination; + nsTArray> mIIRFilters; + AudioDoubleArray mFeedforward; + AudioDoubleArray mFeedback; + uint64_t mWindowID; +}; + +IIRFilterNode::IIRFilterNode(AudioContext* aContext, + const mozilla::dom::binding_detail::AutoSequence& aFeedforward, + const mozilla::dom::binding_detail::AutoSequence& aFeedback) + : AudioNode(aContext, + 2, + ChannelCountMode::Max, + ChannelInterpretation::Speakers) +{ + mFeedforward.SetLength(aFeedforward.Length()); + PodCopy(mFeedforward.Elements(), aFeedforward.Elements(), aFeedforward.Length()); + mFeedback.SetLength(aFeedback.Length()); + PodCopy(mFeedback.Elements(), aFeedback.Elements(), aFeedback.Length()); + + // Scale coefficients -- we guarantee that mFeedback != 0 when creating + // the IIRFilterNode. + double scale = mFeedback[0]; + double* elements = mFeedforward.Elements(); + for (size_t i = 0; i < mFeedforward.Length(); ++i) { + elements[i] /= scale; + } + + elements = mFeedback.Elements(); + for (size_t i = 0; i < mFeedback.Length(); ++i) { + elements[i] /= scale; + } + + // We check that this is exactly equal to one later in blink/IIRFilter.cpp + elements[0] = 1.0; + + uint64_t windowID = aContext->GetParentObject()->WindowID(); + IIRFilterNodeEngine* engine = new IIRFilterNodeEngine(this, aContext->Destination(), mFeedforward, mFeedback, windowID); + mStream = AudioNodeStream::Create(aContext, engine, + AudioNodeStream::NO_STREAM_FLAGS); +} + +IIRFilterNode::~IIRFilterNode() +{ +} + +size_t +IIRFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); + return amount; +} + +size_t +IIRFilterNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); +} + +JSObject* +IIRFilterNode::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return IIRFilterNodeBinding::Wrap(aCx, this, aGivenProto); +} + +void +IIRFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz, + const Float32Array& aMagResponse, + const Float32Array& aPhaseResponse) +{ + aFrequencyHz.ComputeLengthAndData(); + aMagResponse.ComputeLengthAndData(); + aPhaseResponse.ComputeLengthAndData(); + + uint32_t length = std::min(std::min(aFrequencyHz.Length(), + aMagResponse.Length()), + aPhaseResponse.Length()); + if (!length) { + return; + } + + auto frequencies = MakeUnique(length); + float* frequencyHz = aFrequencyHz.Data(); + const double nyquist = Context()->SampleRate() * 0.5; + + // Normalize the frequencies + for (uint32_t i = 0; i < length; ++i) { + if (frequencyHz[i] >= 0 && frequencyHz[i] <= nyquist) { + frequencies[i] = static_cast(frequencyHz[i] / nyquist); + } else { + frequencies[i] = std::numeric_limits::quiet_NaN(); + } + } + + blink::IIRFilter filter(&mFeedforward, &mFeedback); + filter.getFrequencyResponse(int(length), frequencies.get(), aMagResponse.Data(), aPhaseResponse.Data()); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/webaudio/IIRFilterNode.h b/dom/media/webaudio/IIRFilterNode.h new file mode 100644 index 0000000000..78546c3e54 --- /dev/null +++ b/dom/media/webaudio/IIRFilterNode.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 IIRFilterNode_h_ +#define IIRFilterNode_h_ + +#include "AudioNode.h" +#include "AudioParam.h" +#include "mozilla/dom/IIRFilterNodeBinding.h" + +namespace mozilla { +namespace dom { + +class AudioContext; + +class IIRFilterNode final : public AudioNode +{ +public: + explicit IIRFilterNode(AudioContext* aContext, + const mozilla::dom::binding_detail::AutoSequence& aFeedforward, + const mozilla::dom::binding_detail::AutoSequence& aFeedback); + + NS_DECL_ISUPPORTS_INHERITED + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + + void GetFrequencyResponse(const Float32Array& aFrequencyHz, + const Float32Array& aMagResponse, + const Float32Array& aPhaseResponse); + + const char* NodeType() const override + { + return "IIRFilterNode"; + } + + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override; + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override; + +protected: + virtual ~IIRFilterNode(); + +private: + nsTArray mFeedback; + nsTArray mFeedforward; +}; + +} // namespace dom +} // namespace mozilla + +#endif + diff --git a/dom/media/webaudio/MediaBufferDecoder.cpp b/dom/media/webaudio/MediaBufferDecoder.cpp index 262395bb9e..97fcbd70f6 100644 --- a/dom/media/webaudio/MediaBufferDecoder.cpp +++ b/dom/media/webaudio/MediaBufferDecoder.cpp @@ -122,7 +122,7 @@ private: mDecodeJob.OnFailure(aErrorCode); } else { // Take extra care to cleanup on the main thread - NS_DispatchToMainThread(NS_NewRunnableMethod(this, &MediaDecodeTask::Cleanup)); + NS_DispatchToMainThread(NewRunnableMethod(this, &MediaDecodeTask::Cleanup)); nsCOMPtr event = new ReportResultTask(mDecodeJob, &WebAudioDecodeJob::OnFailure, aErrorCode); diff --git a/dom/media/webaudio/WebAudioUtils.cpp b/dom/media/webaudio/WebAudioUtils.cpp index db9fbc3846..6289f803b3 100644 --- a/dom/media/webaudio/WebAudioUtils.cpp +++ b/dom/media/webaudio/WebAudioUtils.cpp @@ -8,6 +8,10 @@ #include "AudioNodeStream.h" #include "blink/HRTFDatabaseLoader.h" +#include "nsContentUtils.h" +#include "nsIConsoleService.h" +#include "nsIScriptError.h" + namespace mozilla { LazyLogModule gWebAudioAPILog("WebAudioAPI"); @@ -89,5 +93,59 @@ WebAudioUtils::SpeexResamplerProcess(SpeexResamplerState* aResampler, #endif } +void +WebAudioUtils::LogToDeveloperConsole(uint64_t aWindowID, const char* aKey) +{ + // This implementation is derived from dom/media/VideoUtils.cpp, but we + // use a windowID so that the message is delivered to the developer console. + // It is similar to ContentUtils::ReportToConsole, but also works off main + // thread. + if (!NS_IsMainThread()) { + nsCOMPtr task = + NS_NewRunnableFunction([aWindowID, aKey]() { LogToDeveloperConsole(aWindowID, aKey); }); + NS_DispatchToMainThread(task.forget(), NS_DISPATCH_NORMAL); + return; + } + + nsCOMPtr console( + do_GetService("@mozilla.org/consoleservice;1")); + if (!console) { + NS_WARNING("Failed to log message to console."); + return; + } + + nsAutoCString spec; + uint32_t aLineNumber, aColumnNumber; + JSContext *cx = nsContentUtils::GetCurrentJSContext(); + if (cx) { + nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber, &aColumnNumber); + } + + nsresult rv; + nsCOMPtr errorObject = + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv); + if (!errorObject) { + NS_WARNING("Failed to log message to console."); + return; + } + + nsXPIDLString result; + rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + aKey, result); + + if (NS_FAILED(rv)) { + NS_WARNING("Failed to log message to console."); + return; + } + + errorObject->InitWithWindowID(result, + NS_ConvertUTF8toUTF16(spec), + EmptyString(), + aLineNumber, aColumnNumber, + nsIScriptError::warningFlag, "Web Audio", + aWindowID); + console->LogMessage(errorObject); +} + } // namespace dom } // namespace mozilla diff --git a/dom/media/webaudio/WebAudioUtils.h b/dom/media/webaudio/WebAudioUtils.h index dcf0037122..c0b27b8379 100644 --- a/dom/media/webaudio/WebAudioUtils.h +++ b/dom/media/webaudio/WebAudioUtils.h @@ -225,6 +225,10 @@ namespace WebAudioUtils { uint32_t aChannel, const int16_t* aIn, uint32_t* aInLen, int16_t* aOut, uint32_t* aOutLen); + + void + LogToDeveloperConsole(uint64_t aWindowID, const char* aKey); + } // namespace WebAudioUtils } // namespace dom diff --git a/dom/media/webaudio/blink/Biquad.cpp b/dom/media/webaudio/blink/Biquad.cpp index 38c92ff113..fbc4cf0776 100644 --- a/dom/media/webaudio/blink/Biquad.cpp +++ b/dom/media/webaudio/blink/Biquad.cpp @@ -76,6 +76,7 @@ void Biquad::process(const float* sourceP, float* destP, size_t framesToProcess) // Avoid introducing a stream of subnormals when input is silent and the // tail approaches zero. + // TODO: Remove this code when Bug 1157635 is fixed. if (x1 == 0.0 && x2 == 0.0 && (y1 != 0.0 || y2 != 0.0) && fabs(y1) < FLT_MIN && fabs(y2) < FLT_MIN) { // Flush future values to zero (until there is new input). diff --git a/dom/media/webaudio/blink/IIRFilter.cpp b/dom/media/webaudio/blink/IIRFilter.cpp new file mode 100644 index 0000000000..3bfafbce36 --- /dev/null +++ b/dom/media/webaudio/blink/IIRFilter.cpp @@ -0,0 +1,158 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "IIRFilter.h" + +#include + +namespace blink { + +// The length of the memory buffers for the IIR filter. This MUST be a power of two and must be +// greater than the possible length of the filter coefficients. +const int kBufferLength = 32; +static_assert(kBufferLength >= IIRFilter::kMaxOrder + 1, + "Internal IIR buffer length must be greater than maximum IIR Filter order."); + +IIRFilter::IIRFilter(const AudioDoubleArray* feedforwardCoef, const AudioDoubleArray* feedbackCoef) + : m_bufferIndex(0) + , m_feedback(feedbackCoef) + , m_feedforward(feedforwardCoef) +{ + m_xBuffer.SetLength(kBufferLength); + m_yBuffer.SetLength(kBufferLength); + reset(); +} + +IIRFilter::~IIRFilter() +{ +} + +void IIRFilter::reset() +{ + memset(m_xBuffer.Elements(), 0, m_xBuffer.Length() * sizeof(double)); + memset(m_yBuffer.Elements(), 0, m_yBuffer.Length() * sizeof(double)); +} + +static std::complex evaluatePolynomial(const double* coef, std::complex z, int order) +{ + // Use Horner's method to evaluate the polynomial P(z) = sum(coef[k]*z^k, k, 0, order); + std::complex result = 0; + + for (int k = order; k >= 0; --k) + result = result * z + std::complex(coef[k]); + + return result; +} + +void IIRFilter::process(const float* sourceP, float* destP, size_t framesToProcess) +{ + // Compute + // + // y[n] = sum(b[k] * x[n - k], k = 0, M) - sum(a[k] * y[n - k], k = 1, N) + // + // where b[k] are the feedforward coefficients and a[k] are the feedback coefficients of the + // filter. + + // This is a Direct Form I implementation of an IIR Filter. Should we consider doing a + // different implementation such as Transposed Direct Form II? + const double* feedback = m_feedback->Elements(); + const double* feedforward = m_feedforward->Elements(); + + MOZ_ASSERT(feedback); + MOZ_ASSERT(feedforward); + + // Sanity check to see if the feedback coefficients have been scaled appropriately. It must + // be EXACTLY 1! + MOZ_ASSERT(feedback[0] == 1); + + int feedbackLength = m_feedback->Length(); + int feedforwardLength = m_feedforward->Length(); + int minLength = std::min(feedbackLength, feedforwardLength); + + double* xBuffer = m_xBuffer.Elements(); + double* yBuffer = m_yBuffer.Elements(); + + for (size_t n = 0; n < framesToProcess; ++n) { + // To help minimize roundoff, we compute using double's, even though the filter coefficients + // only have single precision values. + double yn = feedforward[0] * sourceP[n]; + + // Run both the feedforward and feedback terms together, when possible. + for (int k = 1; k < minLength; ++k) { + int n = (m_bufferIndex - k) & (kBufferLength - 1); + yn += feedforward[k] * xBuffer[n]; + yn -= feedback[k] * yBuffer[n]; + } + + // Handle any remaining feedforward or feedback terms. + for (int k = minLength; k < feedforwardLength; ++k) + yn += feedforward[k] * xBuffer[(m_bufferIndex - k) & (kBufferLength - 1)]; + + for (int k = minLength; k < feedbackLength; ++k) + yn -= feedback[k] * yBuffer[(m_bufferIndex - k) & (kBufferLength - 1)]; + + // Save the current input and output values in the memory buffers for the next output. + m_xBuffer[m_bufferIndex] = sourceP[n]; + m_yBuffer[m_bufferIndex] = yn; + + m_bufferIndex = (m_bufferIndex + 1) & (kBufferLength - 1); + + // Avoid introducing a stream of subnormals + // TODO: Remove this code when Bug 1157635 is fixed. + if (fabs(yn) >= FLT_MIN) { + destP[n] = yn; + } else { + destP[n] = 0.0; + } + } +} + +void IIRFilter::getFrequencyResponse(int nFrequencies, const float* frequency, float* magResponse, float* phaseResponse) +{ + // Evaluate the z-transform of the filter at the given normalized frequencies from 0 to 1. (One + // corresponds to the Nyquist frequency.) + // + // The z-tranform of the filter is + // + // H(z) = sum(b[k]*z^(-k), k, 0, M) / sum(a[k]*z^(-k), k, 0, N); + // + // The desired frequency response is H(exp(j*omega)), where omega is in [0, 1). + // + // Let P(x) = sum(c[k]*x^k, k, 0, P) be a polynomial of order P. Then each of the sums in H(z) + // is equivalent to evaluating a polynomial at the point 1/z. + + for (int k = 0; k < nFrequencies; ++k) { + // zRecip = 1/z = exp(-j*frequency) + double omega = -M_PI * frequency[k]; + std::complex zRecip = std::complex(cos(omega), sin(omega)); + + std::complex numerator = evaluatePolynomial(m_feedforward->Elements(), zRecip, m_feedforward->Length() - 1); + std::complex denominator = evaluatePolynomial(m_feedback->Elements(), zRecip, m_feedback->Length() - 1); + std::complex response = numerator / denominator; + magResponse[k] = static_cast(abs(response)); + phaseResponse[k] = static_cast(atan2(imag(response), real(response))); + } +} + +bool IIRFilter::buffersAreZero() +{ + double* xBuffer = m_xBuffer.Elements(); + double* yBuffer = m_yBuffer.Elements(); + + for (size_t k = 0; k < m_feedforward->Length(); ++k) { + if (xBuffer[(m_bufferIndex - k) & (kBufferLength - 1)] != 0.0) { + return false; + } + } + + for (size_t k = 0; k < m_feedback->Length(); ++k) { + if (fabs(yBuffer[(m_bufferIndex - k) & (kBufferLength - 1)]) >= FLT_MIN) { + return false; + } + } + + return true; +} + +} // namespace blink diff --git a/dom/media/webaudio/blink/IIRFilter.h b/dom/media/webaudio/blink/IIRFilter.h new file mode 100644 index 0000000000..5656d959a4 --- /dev/null +++ b/dom/media/webaudio/blink/IIRFilter.h @@ -0,0 +1,58 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IIRFilter_h +#define IIRFilter_h + +typedef nsTArray AudioDoubleArray; + +namespace blink { + +class IIRFilter final { +public: + // The maximum IIR filter order. This also limits the number of feedforward coefficients. The + // maximum number of coefficients is 20 according to the spec. + const static size_t kMaxOrder = 19; + IIRFilter(const AudioDoubleArray* feedforwardCoef, const AudioDoubleArray* feedbackCoef); + ~IIRFilter(); + + void process(const float* sourceP, float* destP, size_t framesToProcess); + + void reset(); + + void getFrequencyResponse(int nFrequencies, + const float* frequency, + float* magResponse, + float* phaseResponse); + + bool buffersAreZero(); + +private: + // Filter memory + // + // For simplicity, we assume |m_xBuffer| and |m_yBuffer| have the same length, and the length is + // a power of two. Since the number of coefficients has a fixed upper length, the size of + // xBuffer and yBuffer is fixed. |m_xBuffer| holds the old input values and |m_yBuffer| holds + // the old output values needed to compute the new output value. + // + // m_yBuffer[m_bufferIndex] holds the most recent output value, say, y[n]. Then + // m_yBuffer[m_bufferIndex - k] is y[n - k]. Similarly for m_xBuffer. + // + // To minimize roundoff, these arrays are double's instead of floats. + AudioDoubleArray m_xBuffer; + AudioDoubleArray m_yBuffer; + + // Index into the xBuffer and yBuffer arrays where the most current x and y values should be + // stored. xBuffer[bufferIndex] corresponds to x[n], the current x input value and + // yBuffer[bufferIndex] is where y[n], the current output value. + int m_bufferIndex; + + // Coefficients of the IIR filter. + const AudioDoubleArray* m_feedback; + const AudioDoubleArray* m_feedforward; +}; + +} // namespace blink + +#endif // IIRFilter_h diff --git a/dom/media/webaudio/blink/ReverbConvolver.cpp b/dom/media/webaudio/blink/ReverbConvolver.cpp index 712ba6420d..e739400aee 100644 --- a/dom/media/webaudio/blink/ReverbConvolver.cpp +++ b/dom/media/webaudio/blink/ReverbConvolver.cpp @@ -31,13 +31,6 @@ using namespace mozilla; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(WebCore::ReverbConvolver* obj) {} - static void ReleaseCallee(WebCore::ReverbConvolver* obj) {} -}; - namespace WebCore { const int InputBufferSize = 8 * 16384; @@ -158,8 +151,8 @@ ReverbConvolver::ReverbConvolver(const float* impulseResponseData, NS_WARNING("Cannot start convolver thread."); return; } - m_backgroundThread.message_loop()->PostTask( - NewRunnableMethod(this, &ReverbConvolver::backgroundThreadEntry)); + m_backgroundThread.message_loop()->PostTask(NewNonOwningRunnableMethod(this, + &ReverbConvolver::backgroundThreadEntry)); } } diff --git a/dom/media/webaudio/blink/moz.build b/dom/media/webaudio/blink/moz.build index 5af26ad282..c0e1b0f234 100644 --- a/dom/media/webaudio/blink/moz.build +++ b/dom/media/webaudio/blink/moz.build @@ -14,6 +14,7 @@ UNIFIED_SOURCES += [ 'HRTFElevation.cpp', 'HRTFKernel.cpp', 'HRTFPanner.cpp', + 'IIRFilter.cpp', 'PeriodicWave.cpp', 'Reverb.cpp', 'ReverbAccumulationBuffer.cpp', diff --git a/dom/media/webaudio/moz.build b/dom/media/webaudio/moz.build index 1c66d72fcb..71d3496f9b 100644 --- a/dom/media/webaudio/moz.build +++ b/dom/media/webaudio/moz.build @@ -50,6 +50,7 @@ EXPORTS.mozilla.dom += [ 'DelayNode.h', 'DynamicsCompressorNode.h', 'GainNode.h', + 'IIRFilterNode.h', 'MediaElementAudioSourceNode.h', 'MediaStreamAudioDestinationNode.h', 'MediaStreamAudioSourceNode.h', @@ -86,6 +87,7 @@ UNIFIED_SOURCES += [ 'DynamicsCompressorNode.cpp', 'FFTBlock.cpp', 'GainNode.cpp', + 'IIRFilterNode.cpp', 'MediaBufferDecoder.cpp', 'MediaElementAudioSourceNode.cpp', 'MediaStreamAudioDestinationNode.cpp', diff --git a/dom/media/webspeech/synth/nsSpeechTask.cpp b/dom/media/webspeech/synth/nsSpeechTask.cpp index 9e4e821cf0..0e8e916acc 100644 --- a/dom/media/webspeech/synth/nsSpeechTask.cpp +++ b/dom/media/webspeech/synth/nsSpeechTask.cpp @@ -58,9 +58,16 @@ public: switch (event) { case EVENT_FINISHED: { - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished); - aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); + if (!mStarted) { + mStarted = true; + nsCOMPtr startRunnable = + NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted); + aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget()); + } + + nsCOMPtr endRunnable = + NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished); + aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget()); } break; case EVENT_REMOVED: @@ -78,7 +85,7 @@ public: if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) { mStarted = true; nsCOMPtr event = - NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted); + NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); } } @@ -705,9 +712,16 @@ nsSpeechTask::CreateAudioChannelAgent() mAudioChannelAgent->InitWithWeakCallback(mUtterance->GetOwner(), static_cast(AudioChannelService::GetDefaultAudioChannel()), this); - float volume = 0.0f; - bool muted = true; - mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted); + + AudioPlaybackConfig config; + nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config, + AudioChannelService::AudibleState::eAudible); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + WindowVolumeChanged(config.mVolume, config.mMuted); + WindowSuspendChanged(config.mSuspend); } void @@ -726,6 +740,19 @@ nsSpeechTask::WindowVolumeChanged(float aVolume, bool aMuted) return NS_OK; } +NS_IMETHODIMP +nsSpeechTask::WindowSuspendChanged(nsSuspendedTypes aSuspend) +{ + if (aSuspend == nsISuspendedTypes::NONE_SUSPENDED && + mUtterance->mPaused) { + Resume(); + } else if (aSuspend != nsISuspendedTypes::NONE_SUSPENDED && + !mUtterance->mPaused) { + Pause(); + } + return NS_OK; +} + NS_IMETHODIMP nsSpeechTask::WindowAudioCaptureChanged(bool aCapture) { diff --git a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp index 6525208055..f14594ae5c 100644 --- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp +++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp @@ -657,12 +657,11 @@ nsSynthVoiceRegistry::SpeakUtterance(SpeechSynthesisUtterance& aUtterance, RefPtr service = AudioChannelService::GetOrCreate(); if (service) { if (nsCOMPtr topWindow = aUtterance.GetOwner()) { - float audioVolume = 1.0f; - bool muted = false; - service->GetState(topWindow->GetOuterWindow(), - static_cast(AudioChannelService::GetDefaultAudioChannel()), - &audioVolume, &muted); - volume = muted ? 0.0f : audioVolume * volume; + // TODO : use audio channel agent, open new bug to fix it. + uint32_t channel = static_cast(AudioChannelService::GetDefaultAudioChannel()); + AudioPlaybackConfig config = service->GetMediaConfig(topWindow->GetOuterWindow(), + channel); + volume = config.mMuted ? 0.0f : config.mVolume * volume; } } diff --git a/dom/media/webspeech/synth/pico/nsPicoService.cpp b/dom/media/webspeech/synth/pico/nsPicoService.cpp index c68b4f853c..cd91ac4266 100644 --- a/dom/media/webspeech/synth/pico/nsPicoService.cpp +++ b/dom/media/webspeech/synth/pico/nsPicoService.cpp @@ -469,7 +469,7 @@ nsPicoService::Observe(nsISupports* aSubject, const char* aTopic, DebugOnly rv = NS_NewNamedThread("Pico Worker", getter_AddRefs(mThread)); MOZ_ASSERT(NS_SUCCEEDED(rv)); return mThread->Dispatch( - NS_NewRunnableMethod(this, &nsPicoService::Init), NS_DISPATCH_NORMAL); + NewRunnableMethod(this, &nsPicoService::Init), NS_DISPATCH_NORMAL); } // nsISpeechService @@ -576,7 +576,7 @@ nsPicoService::Init() rv = dirIterator->HasMoreElements(&hasMoreElements); } - NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsPicoService::RegisterVoices)); + NS_DispatchToMainThread(NewRunnableMethod(this, &nsPicoService::RegisterVoices)); } void diff --git a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp index bf39eb0e82..028b5355b2 100644 --- a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp +++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp @@ -268,7 +268,7 @@ speechd_cb(size_t msg_id, size_t client_id, SPDNotificationType state) if (service) { NS_DispatchToMainThread( - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( service, &SpeechDispatcherService::EventNotify, static_cast(msg_id), state)); } @@ -305,7 +305,7 @@ SpeechDispatcherService::Init() getter_AddRefs(mInitThread)); MOZ_ASSERT(NS_SUCCEEDED(rv)); rv = mInitThread->Dispatch( - NS_NewRunnableMethod(this, &SpeechDispatcherService::Setup), NS_DISPATCH_NORMAL); + NewRunnableMethod(this, &SpeechDispatcherService::Setup), NS_DISPATCH_NORMAL); MOZ_ASSERT(NS_SUCCEEDED(rv)); } @@ -406,7 +406,7 @@ SpeechDispatcherService::Setup() } } - NS_DispatchToMainThread(NS_NewRunnableMethod(this, &SpeechDispatcherService::RegisterVoices)); + NS_DispatchToMainThread(NewRunnableMethod(this, &SpeechDispatcherService::RegisterVoices)); //mInitialized = true; } @@ -511,10 +511,10 @@ SpeechDispatcherService::Speak(const nsAString& aText, const nsAString& aUri, // Speech dispatcher does not work well with empty strings. // In that case, don't send empty string to speechd, // and just emulate a speechd start and end event. - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs( + NS_DispatchToMainThread(NewRunnableMethod( callback, &SpeechDispatcherCallback::OnSpeechEvent, SPD_EVENT_BEGIN)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs( + NS_DispatchToMainThread(NewRunnableMethod( callback, &SpeechDispatcherCallback::OnSpeechEvent, SPD_EVENT_END)); } diff --git a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp index 4e34e72008..582ff35510 100644 --- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp +++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp @@ -356,7 +356,7 @@ nsFakeSynthServices::Observe(nsISupports* aSubject, const char* aTopic, } if (Preferences::GetBool("media.webspeech.synth.test")) { - NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsFakeSynthServices::Init)); + NS_DispatchToMainThread(NewRunnableMethod(this, &nsFakeSynthServices::Init)); } return NS_OK; diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 553af9c353..26d8bca8b2 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -46,6 +46,7 @@ #include "nsProxyRelease.h" #include "nsServiceManagerUtils.h" #include "nsStructuredCloneContainer.h" +#include "nsThreadUtils.h" #include "nsToolkitCompsCID.h" #include "nsXULAppAPI.h" #include "ServiceWorkerManager.h" @@ -646,9 +647,8 @@ NotificationPermissionRequest::GetRequester(nsIContentPermissionRequester** aReq inline nsresult NotificationPermissionRequest::DispatchResolvePromise() { - nsCOMPtr resolveRunnable = NS_NewRunnableMethod(this, - &NotificationPermissionRequest::ResolvePromise); - return NS_DispatchToMainThread(resolveRunnable); + return NS_DispatchToMainThread(NewRunnableMethod(this, + &NotificationPermissionRequest::ResolvePromise)); } nsresult diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index de9e97b574..e2b38d19f4 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -107,6 +107,7 @@ using mozilla::plugins::PluginModuleContentParent; #endif #include "nsIAudioChannelAgent.h" +#include "AudioChannelService.h" using namespace mozilla; using namespace mozilla::plugins::parent; @@ -2441,14 +2442,20 @@ _setvalue(NPP npp, NPPVariable variable, void *result) return NPERR_NO_ERROR; } } else { - float volume = 0.0; - bool muted = true; - rv = agent->NotifyStartedPlaying(&volume, &muted); + + dom::AudioPlaybackConfig config; + rv = agent->NotifyStartedPlaying(&config, + dom::AudioChannelService::AudibleState::eAudible); if (NS_WARN_IF(NS_FAILED(rv))) { return NPERR_NO_ERROR; } - rv = inst->WindowVolumeChanged(volume, muted); + rv = inst->WindowVolumeChanged(config.mVolume, config.mMuted); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NPERR_NO_ERROR; + } + + rv = inst->WindowSuspendChanged(config.mSuspend); if (NS_WARN_IF(NS_FAILED(rv))) { return NPERR_NO_ERROR; } diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index d44c7b2871..1d92f2b878 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -891,7 +891,7 @@ already_AddRefed nsNPAPIPluginInstance::CreateSurfaceText return nullptr; } - nsCOMPtr frameCallback = NS_NewRunnableMethod(this, &nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable); + nsCOMPtr frameCallback = NewRunnableMethod(this, &nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable); surface->SetFrameAvailableCallback(frameCallback); return surface.forget(); } @@ -1848,6 +1848,15 @@ nsNPAPIPluginInstance::WindowVolumeChanged(float aVolume, bool aMuted) return rv; } +NS_IMETHODIMP +nsNPAPIPluginInstance::WindowSuspendChanged(nsSuspendedTypes aSuspend) +{ + // It doesn't support suspended, so we just do something like mute/unmute. + WindowVolumeChanged(1.0, /* useless */ + aSuspend != nsISuspendedTypes::NONE_SUSPENDED); + return NS_OK; +} + NS_IMETHODIMP nsNPAPIPluginInstance::WindowAudioCaptureChanged(bool aCapture) { diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index 5de8453a08..a6ed968bde 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -112,13 +112,6 @@ static const TCHAR kPluginIgnoreSubclassProperty[] = TEXT("PluginIgnoreSubclassP #include "PluginUtilsOSX.h" #endif // defined(XP_MACOSX) -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(PluginInstanceChild* obj) { } - static void ReleaseCallee(PluginInstanceChild* obj) { } -}; - /** * We can't use gfxPlatform::CreateDrawTargetForSurface() because calling * gfxPlatform::GetPlatform() instantiates the prefs service, and that's not @@ -3299,11 +3292,8 @@ PluginInstanceChild::RecvAsyncSetWindow(const gfxSurfaceType& aSurfaceType, // RPC call, and both Flash and Java don't expect to receive setwindow calls // at arbitrary times. mCurrentAsyncSetWindowTask = - NewRunnableMethod - (this, &PluginInstanceChild::DoAsyncSetWindow, - aSurfaceType, aWindow, true); + NewNonOwningCancelableRunnableMethod + (this, &PluginInstanceChild::DoAsyncSetWindow, aSurfaceType, aWindow, true); RefPtr addrefedTask = mCurrentAsyncSetWindowTask; MessageLoop::current()->PostTask(addrefedTask.forget()); @@ -4223,7 +4213,7 @@ PluginInstanceChild::AsyncShowPluginFrame(void) } mCurrentInvalidateTask = - NewRunnableMethod(this, &PluginInstanceChild::InvalidateRectDelayed); + NewNonOwningCancelableRunnableMethod(this, &PluginInstanceChild::InvalidateRectDelayed); RefPtr addrefedTask = mCurrentInvalidateTask; MessageLoop::current()->PostTask(addrefedTask.forget()); } diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 1aa5abdc54..ddfcd31cb9 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -118,13 +118,6 @@ PluginInstanceParent::LookupPluginInstanceByID(uintptr_t aId) } #endif -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(PluginInstanceParent* obj) { } - static void ReleaseCallee(PluginInstanceParent* obj) { } -}; - PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent, NPP npp, const nsCString& aMimeType, @@ -1223,7 +1216,7 @@ PluginInstanceParent::ScheduleScrollCapture(int aTimeout) } CAPTURE_LOG("delayed scroll capture requested."); mCaptureRefreshTask = - NewRunnableMethod(this, &PluginInstanceParent::ScheduledUpdateScrollCaptureCallback); + NewNonOwningCancelableRunnableMethod(this, &PluginInstanceParent::ScheduledUpdateScrollCaptureCallback); RefPtr addrefedTask = mCaptureRefreshTask; MessageLoop::current()->PostDelayedTask(addrefedTask.forget(), kScrollCaptureDelayMs); diff --git a/dom/plugins/ipc/PluginModuleChild.cpp b/dom/plugins/ipc/PluginModuleChild.cpp index 0070b825d0..86d6f6697d 100644 --- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -104,13 +104,6 @@ static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr; static HWND sBrowserHwnd = nullptr; #endif -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(PluginModuleChild* obj) { } - static void ReleaseCallee(PluginModuleChild* obj) { } -}; - /* static */ PluginModuleChild* PluginModuleChild::CreateForContentProcess(mozilla::ipc::Transport* aTransport, diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index 2fdfddbc46..16ce362593 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -96,14 +96,6 @@ static const char kHangUIMinDisplayPref[] = "dom.ipc.plugins.hangUIMinDisplaySec #define CHILD_TIMEOUT_PREF kChildTimeoutPref #endif -template<> -struct RunnableMethodTraits -{ - typedef mozilla::plugins::PluginModuleParent Class; - static void RetainCallee(Class* obj) { } - static void ReleaseCallee(Class* obj) { } -}; - bool mozilla::plugins::SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent, diff --git a/dom/plugins/ipc/PluginProcessParent.cpp b/dom/plugins/ipc/PluginProcessParent.cpp index 955b030e40..6842cd85ec 100644 --- a/dom/plugins/ipc/PluginProcessParent.cpp +++ b/dom/plugins/ipc/PluginProcessParent.cpp @@ -27,13 +27,6 @@ using mozilla::plugins::LaunchCompleteTask; using mozilla::plugins::PluginProcessParent; using base::ProcessArchitecture; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(PluginProcessParent* obj) { } - static void ReleaseCallee(PluginProcessParent* obj) { } -}; - PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath) : GeckoChildProcessHost(GeckoProcessType_Plugin), mPluginFilePath(aPluginFilePath), @@ -194,7 +187,7 @@ PluginProcessParent::Delete() return; } - ioLoop->PostTask(NewRunnableMethod(this, &PluginProcessParent::Delete)); + ioLoop->PostTask(NewNonOwningRunnableMethod(this, &PluginProcessParent::Delete)); } void diff --git a/dom/presentation/PresentationAvailability.cpp b/dom/presentation/PresentationAvailability.cpp index f599d91957..4195b45039 100644 --- a/dom/presentation/PresentationAvailability.cpp +++ b/dom/presentation/PresentationAvailability.cpp @@ -107,11 +107,10 @@ PresentationAvailability::Value() const NS_IMETHODIMP PresentationAvailability::NotifyAvailableChange(bool aIsAvailable) { - nsCOMPtr runnable = - NS_NewRunnableMethodWithArg(this, - &PresentationAvailability::UpdateAvailabilityAndDispatchEvent, - aIsAvailable); - return NS_DispatchToCurrentThread(runnable); + return NS_DispatchToCurrentThread(NewRunnableMethod + (this, + &PresentationAvailability::UpdateAvailabilityAndDispatchEvent, + aIsAvailable)); } void diff --git a/dom/presentation/PresentationDeviceManager.cpp b/dom/presentation/PresentationDeviceManager.cpp index 474c83b73f..ce8c6fde9c 100644 --- a/dom/presentation/PresentationDeviceManager.cpp +++ b/dom/presentation/PresentationDeviceManager.cpp @@ -155,7 +155,7 @@ PresentationDeviceManager::GetAvailableDevices(nsIArray** aRetVal) // Bug 1194049: some providers may discontinue discovery after timeout. // Call |ForceDiscovery()| here to make sure device lists are updated. NS_DispatchToMainThread( - NS_NewRunnableMethod(this, &PresentationDeviceManager::ForceDiscovery)); + NewRunnableMethod(this, &PresentationDeviceManager::ForceDiscovery)); nsCOMPtr devices = do_CreateInstance(NS_ARRAY_CONTRACTID); for (uint32_t i = 0; i < mDevices.Length(); ++i) { diff --git a/dom/presentation/PresentationSessionInfo.cpp b/dom/presentation/PresentationSessionInfo.cpp index 030dd2a950..8d1a6678be 100644 --- a/dom/presentation/PresentationSessionInfo.cpp +++ b/dom/presentation/PresentationSessionInfo.cpp @@ -112,9 +112,9 @@ PresentationNetworkHelper::OnGetWifiIPAddress(const nsACString& aIPAddress) MOZ_ASSERT(mFunc); NS_DispatchToMainThread( - NS_NewRunnableMethodWithArg(mInfo, - mFunc, - aIPAddress)); + NewRunnableMethod(mInfo, + mFunc, + aIPAddress)); return NS_OK; } @@ -550,7 +550,7 @@ PresentationControllingInfo::GetAddress() // To make consistent code sequence, following function call is dispatched // into main thread instead of calling it directly. NS_DispatchToMainThread( - NS_NewRunnableMethodWithArg( + NewRunnableMethod( this, &PresentationControllingInfo::OnGetAddress, NS_ConvertUTF16toUTF8(ip))); @@ -570,7 +570,7 @@ PresentationControllingInfo::GetAddress() #elif defined(MOZ_MULET) // In simulator,we need to use the "127.0.0.1" as target address. NS_DispatchToMainThread( - NS_NewRunnableMethodWithArg( + NewRunnableMethod( this, &PresentationControllingInfo::OnGetAddress, "127.0.0.1")); @@ -579,7 +579,7 @@ PresentationControllingInfo::GetAddress() // TODO Get host IP via other platforms. NS_DispatchToMainThread( - NS_NewRunnableMethodWithArg( + NewRunnableMethod( this, &PresentationControllingInfo::OnGetAddress, EmptyCString())); diff --git a/dom/presentation/PresentationTCPSessionTransport.cpp b/dom/presentation/PresentationTCPSessionTransport.cpp index a2cd9b3a0b..111242f4d7 100644 --- a/dom/presentation/PresentationTCPSessionTransport.cpp +++ b/dom/presentation/PresentationTCPSessionTransport.cpp @@ -111,18 +111,18 @@ PresentationTCPSessionTransport::BuildTCPSenderTransport(nsISocketTransport* aTr nsCOMPtr sessionTransport = do_QueryObject(this); nsCOMPtr onSessionTransportRunnable = - NS_NewRunnableMethodWithArgs + NewRunnableMethod (mListener, &nsIPresentationSessionTransportBuilderListener::OnSessionTransport, sessionTransport); - NS_DispatchToCurrentThread(onSessionTransportRunnable); + NS_DispatchToCurrentThread(onSessionTransportRunnable.forget()); nsCOMPtr setReadyStateRunnable = - NS_NewRunnableMethodWithArgs(this, - &PresentationTCPSessionTransport::SetReadyState, - ReadyState::OPEN); - return NS_DispatchToCurrentThread(setReadyStateRunnable); + NewRunnableMethod(this, + &PresentationTCPSessionTransport::SetReadyState, + ReadyState::OPEN); + return NS_DispatchToCurrentThread(setReadyStateRunnable.forget()); } NS_IMETHODIMP @@ -193,11 +193,11 @@ PresentationTCPSessionTransport::BuildTCPReceiverTransport(nsIPresentationChanne nsCOMPtr sessionTransport = do_QueryObject(this); nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs + NewRunnableMethod (mListener, &nsIPresentationSessionTransportBuilderListener::OnSessionTransport, sessionTransport); - return NS_DispatchToCurrentThread(runnable); + return NS_DispatchToCurrentThread(runnable.forget()); } nsresult diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp index 0880c83b38..07f6710fc8 100644 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp +++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp @@ -721,7 +721,7 @@ MulticastDNSDeviceProvider::OnRegistrationFailed(nsIDNSServiceInfo* aServiceInfo if (aErrorCode == nsIDNSRegistrationListener::ERROR_SERVICE_NOT_RUNNING) { return NS_DispatchToMainThread( - NS_NewRunnableMethod(this, &MulticastDNSDeviceProvider::RegisterService)); + NewRunnableMethod(this, &MulticastDNSDeviceProvider::RegisterService)); } return NS_OK; diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 15ba4c2433..4e02495d13 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -3084,13 +3084,15 @@ QuotaManager::Shutdown() NS_WARNING("Failed to cancel shutdown timer!"); } - // Give clients a chance to cleanup IO thread only objects. - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects); - if (!runnable) { - NS_WARNING("Failed to create runnable!"); - } + // NB: It's very important that runnable is destroyed on this thread + // (i.e. after we join the IO thread) because we can't release the + // QuotaManager on the IO thread. This should probably use + // NewNonOwningRunnableMethod ... + RefPtr runnable = + NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects); + MOZ_ASSERT(runnable); + // Give clients a chance to cleanup IO thread only objects. if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch runnable!"); } @@ -5313,7 +5315,7 @@ Quota::RecvStartIdleMaintenance() QuotaManager* quotaManager = QuotaManager::Get(); if (!quotaManager) { nsCOMPtr callback = - NS_NewRunnableMethod(this, &Quota::StartIdleMaintenance); + NewRunnableMethod(this, &Quota::StartIdleMaintenance); QuotaManager::GetOrCreate(callback); return true; diff --git a/dom/storage/DOMStorage.cpp b/dom/storage/DOMStorage.cpp index 780a0cf698..b4f2acb70b 100644 --- a/dom/storage/DOMStorage.cpp +++ b/dom/storage/DOMStorage.cpp @@ -296,7 +296,7 @@ DOMStorage::CanAccess(nsIPrincipal* aPrincipal) } void -DOMStorage::GetSupportedNames(unsigned, nsTArray& aKeys) +DOMStorage::GetSupportedNames(nsTArray& aKeys) { if (!CanUseStorage(nullptr, this)) { // return just an empty array diff --git a/dom/storage/DOMStorage.h b/dom/storage/DOMStorage.h index d8afedfecb..f7d32775e7 100644 --- a/dom/storage/DOMStorage.h +++ b/dom/storage/DOMStorage.h @@ -81,12 +81,7 @@ public: void GetItem(const nsAString& aKey, nsAString& aResult, ErrorResult& aRv); - bool NameIsEnumerable(const nsAString& aName) const - { - return true; - } - - void GetSupportedNames(unsigned, nsTArray& aKeys); + void GetSupportedNames(nsTArray& aKeys); void NamedGetter(const nsAString& aKey, bool& aFound, nsAString& aResult, ErrorResult& aRv) diff --git a/dom/storage/DOMStorageCache.cpp b/dom/storage/DOMStorageCache.cpp index 2eb2b7da22..6c7aed5b32 100644 --- a/dom/storage/DOMStorageCache.cpp +++ b/dom/storage/DOMStorageCache.cpp @@ -110,8 +110,8 @@ DOMStorageCache::Release(void) } RefPtr > event = - NS_NewNonOwningRunnableMethod(static_cast(this), - &DOMStorageCacheBridge::Release); + NewNonOwningRunnableMethod(static_cast(this), + &DOMStorageCacheBridge::Release); nsresult rv = NS_DispatchToMainThread(event); if (NS_FAILED(rv)) { @@ -281,10 +281,7 @@ DOMStorageCache::KeepAlive() if (!NS_IsMainThread()) { // Timer and the holder must be initialized on the main thread. - RefPtr > event = - NS_NewRunnableMethod(this, &DOMStorageCache::KeepAlive); - - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(this, &DOMStorageCache::KeepAlive)); return; } @@ -564,9 +561,16 @@ DOMStorageCache::Clear(const DOMStorage* aStorage) void DOMStorageCache::CloneFrom(const DOMStorageCache* aThat) { - mLoaded = aThat->mLoaded; + // This will never be called on anything else than SessionStorage. + // This means mData will never be touched on any other thread than + // the main thread and it never went through the loading process. + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mPersistent); + MOZ_ASSERT(!(bool)aThat->mLoaded); + + mLoaded = false; mInitialized = aThat->mInitialized; - mPersistent = aThat->mPersistent; + mPersistent = false; mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive; for (uint32_t i = 0; i < kDataSetCount; ++i) { diff --git a/dom/storage/DOMStorageCache.h b/dom/storage/DOMStorageCache.h index 3ee3ce1e5c..2bd4737f45 100644 --- a/dom/storage/DOMStorageCache.h +++ b/dom/storage/DOMStorageCache.h @@ -15,6 +15,7 @@ #include "nsHashKeys.h" #include "mozilla/Monitor.h" #include "mozilla/Telemetry.h" +#include "mozilla/Atomics.h" #include "nsAutoPtr.h" namespace mozilla { @@ -220,8 +221,9 @@ private: // Flag that is initially false. When the cache is about to work with // the database (i.e. it is persistent) this flags is set to true after // all keys and coresponding values are loaded from the database. - // This flag never goes from true back to false. - bool mLoaded; + // This flag never goes from true back to false. Since this flag is + // critical for mData hashtable synchronization, it's made atomic. + Atomic mLoaded; // Result of load from the database. Valid after mLoaded flag has been set. nsresult mLoadResult; diff --git a/dom/storage/DOMStorageDBThread.cpp b/dom/storage/DOMStorageDBThread.cpp index 05cee4afaf..6491db4728 100644 --- a/dom/storage/DOMStorageDBThread.cpp +++ b/dom/storage/DOMStorageDBThread.cpp @@ -721,7 +721,7 @@ DOMStorageDBThread::NotifyFlushCompletion() #ifdef DOM_STORAGE_TESTS if (!NS_IsMainThread()) { RefPtr > event = - NS_NewNonOwningRunnableMethod(this, &DOMStorageDBThread::NotifyFlushCompletion); + NewNonOwningRunnableMethod(this, &DOMStorageDBThread::NotifyFlushCompletion); NS_DispatchToMainThread(event); return; } diff --git a/dom/svg/SVGFEImageElement.cpp b/dom/svg/SVGFEImageElement.cpp index e085f00895..b2ebfbe0fd 100644 --- a/dom/svg/SVGFEImageElement.cpp +++ b/dom/svg/SVGFEImageElement.cpp @@ -155,7 +155,7 @@ SVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, ClearBrokenState(); RemoveStatesSilently(NS_EVENT_STATE_BROKEN); nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &SVGFEImageElement::MaybeLoadSVGImage)); + NewRunnableMethod(this, &SVGFEImageElement::MaybeLoadSVGImage)); } return rv; diff --git a/dom/svg/SVGImageElement.cpp b/dom/svg/SVGImageElement.cpp index 144e375f2d..6d5af4128e 100644 --- a/dom/svg/SVGImageElement.cpp +++ b/dom/svg/SVGImageElement.cpp @@ -183,7 +183,7 @@ SVGImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, ClearBrokenState(); RemoveStatesSilently(NS_EVENT_STATE_BROKEN); nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &SVGImageElement::MaybeLoadSVGImage)); + NewRunnableMethod(this, &SVGImageElement::MaybeLoadSVGImage)); } return rv; diff --git a/dom/svg/SVGStyleElement.cpp b/dom/svg/SVGStyleElement.cpp index 33437f7ba7..22fb204dff 100644 --- a/dom/svg/SVGStyleElement.cpp +++ b/dom/svg/SVGStyleElement.cpp @@ -78,7 +78,7 @@ SVGStyleElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, NS_ENSURE_SUCCESS(rv, rv); void (SVGStyleElement::*update)() = &SVGStyleElement::UpdateStyleSheetInternal; - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update)); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, update)); return rv; } diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp index 4da9946c95..056a8b0da9 100644 --- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -560,9 +560,9 @@ Telephony::HandleAudioAgentState() } } else if (!activeCall.IsNull() && !mIsAudioStartPlaying) { mIsAudioStartPlaying = true; - float volume; - bool muted; - rv = mAudioAgent->NotifyStartedPlaying(&volume, &muted); + AudioPlaybackConfig config; + rv = mAudioAgent->NotifyStartedPlaying(&config, + AudioChannelService::AudibleState::eAudible); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -576,9 +576,12 @@ Telephony::HandleAudioAgentState() // because the modem have not changed the call state yet. It causes that // the telephony can't be resumed. Therefore, we don't mute the telephony // at the beginning. - volume = 1.0; - muted = false; - rv = WindowVolumeChanged(volume, muted); + rv = WindowVolumeChanged(1.0, false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = WindowSuspendChanged(config.mSuspend); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -710,6 +713,13 @@ Telephony::WindowVolumeChanged(float aVolume, bool aMuted) return NS_OK; } +NS_IMETHODIMP +Telephony::WindowSuspendChanged(nsSuspendedTypes aSuspend) +{ + // Not support yet. + return NS_OK; +} + NS_IMETHODIMP Telephony::WindowAudioCaptureChanged(bool aCapture) { diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 0c8013c1f5..f109679e09 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -697,6 +697,8 @@ var interfaceNamesInGlobalScope = "IDBTransaction", // IMPORTANT: Do not change this list without review from a DOM peer! "IDBVersionChangeEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IIRFilterNode", // IMPORTANT: Do not change this list without review from a DOM peer! "Image", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/tv/TVSource.cpp b/dom/tv/TVSource.cpp index 869315e650..1b78fdf0b6 100644 --- a/dom/tv/TVSource.cpp +++ b/dom/tv/TVSource.cpp @@ -372,9 +372,9 @@ TVSource::DispatchCurrentChannelChangedEvent(TVChannel* aChannel) NS_LITERAL_STRING("currentchannelchanged"), init); nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>(this, - &TVSource::DispatchTVEvent, - event); + NewRunnableMethod>(this, + &TVSource::DispatchTVEvent, + event); return NS_DispatchToCurrentThread(runnable); } @@ -390,9 +390,9 @@ TVSource::DispatchScanningStateChangedEvent(TVScanningState aState, NS_LITERAL_STRING("scanningstatechanged"), init); nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>(this, - &TVSource::DispatchTVEvent, - event); + NewRunnableMethod>(this, + &TVSource::DispatchTVEvent, + event); return NS_DispatchToCurrentThread(runnable); } @@ -406,9 +406,9 @@ TVSource::DispatchEITBroadcastedEvent(const Sequence>& NS_LITERAL_STRING("eitbroadcasted"), init); nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>(this, - &TVSource::DispatchTVEvent, - event); + NewRunnableMethod>(this, + &TVSource::DispatchTVEvent, + event); return NS_DispatchToCurrentThread(runnable); } diff --git a/dom/tv/TVTuner.cpp b/dom/tv/TVTuner.cpp index e1e4412ddc..35f53eb9ac 100644 --- a/dom/tv/TVTuner.cpp +++ b/dom/tv/TVTuner.cpp @@ -315,9 +315,9 @@ TVTuner::DispatchCurrentSourceChangedEvent(TVSource* aSource) NS_LITERAL_STRING("currentsourcechanged"), init); nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>(this, - &TVTuner::DispatchTVEvent, - event); + NewRunnableMethod>(this, + &TVTuner::DispatchTVEvent, + event); return NS_DispatchToCurrentThread(runnable); } diff --git a/dom/webidl/AudioContext.webidl b/dom/webidl/AudioContext.webidl index 654f04ee46..93056ba593 100644 --- a/dom/webidl/AudioContext.webidl +++ b/dom/webidl/AudioContext.webidl @@ -75,6 +75,8 @@ interface AudioContext : EventTarget { [NewObject, Throws] BiquadFilterNode createBiquadFilter(); [NewObject, Throws] + IIRFilterNode createIIRFilter(sequence feedforward, sequence feedback); + [NewObject, Throws] WaveShaperNode createWaveShaper(); [NewObject, Throws] PannerNode createPanner(); diff --git a/dom/webidl/HTMLAllCollection.webidl b/dom/webidl/HTMLAllCollection.webidl index 433ad2aa52..b352138888 100644 --- a/dom/webidl/HTMLAllCollection.webidl +++ b/dom/webidl/HTMLAllCollection.webidl @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Emulates undefined through Codegen.py. */ +[LegacyUnenumerableNamedProperties] interface HTMLAllCollection { readonly attribute unsigned long length; getter Node? (unsigned long index); diff --git a/dom/webidl/HTMLCollection.webidl b/dom/webidl/HTMLCollection.webidl index 178986a86d..f7f020c0ff 100644 --- a/dom/webidl/HTMLCollection.webidl +++ b/dom/webidl/HTMLCollection.webidl @@ -10,6 +10,7 @@ * liability, trademark and document use rules apply. */ +[LegacyUnenumerableNamedProperties] interface HTMLCollection { readonly attribute unsigned long length; getter Element? item(unsigned long index); diff --git a/dom/webidl/HTMLFormElement.webidl b/dom/webidl/HTMLFormElement.webidl index 1581d19fd4..d7964689f3 100644 --- a/dom/webidl/HTMLFormElement.webidl +++ b/dom/webidl/HTMLFormElement.webidl @@ -11,6 +11,8 @@ * and create derivative works of this document. */ +// Should be LegacyUnenumerableNamedProperties. See +// https://bugzilla.mozilla.org/show_bug.cgi?id=1270369 [OverrideBuiltins] interface HTMLFormElement : HTMLElement { [Pure, SetterThrows] diff --git a/dom/webidl/HTMLMediaElement.webidl b/dom/webidl/HTMLMediaElement.webidl index 0385c6a01a..92ce2d6365 100644 --- a/dom/webidl/HTMLMediaElement.webidl +++ b/dom/webidl/HTMLMediaElement.webidl @@ -177,4 +177,6 @@ partial interface HTMLMediaElement { readonly attribute double computedVolume; [Pref="media.useAudioChannelService.testing"] readonly attribute boolean computedMuted; + [Pref="media.useAudioChannelService.testing"] + readonly attribute unsigned long computedSuspended; }; diff --git a/dom/webidl/IIRFilterNode.webidl b/dom/webidl/IIRFilterNode.webidl new file mode 100644 index 0000000000..4442e28ef1 --- /dev/null +++ b/dom/webidl/IIRFilterNode.webidl @@ -0,0 +1,17 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is https://www.w3.org/TR/webaudio + * + * Copyright © 2016 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +interface IIRFilterNode : AudioNode { + void getFrequencyResponse(Float32Array frequencyHz, Float32Array magResponse, Float32Array phaseResponse); +}; + +// Mozilla extension +IIRFilterNode implements AudioNodePassThrough; diff --git a/dom/webidl/Location.webidl b/dom/webidl/Location.webidl index 3d6b39ff9f..fa017156a9 100644 --- a/dom/webidl/Location.webidl +++ b/dom/webidl/Location.webidl @@ -11,7 +11,7 @@ * and create derivative works of this document. */ -[Unforgeable] +[Unforgeable, NonOrdinaryGetPrototypeOf] interface Location { // Bug 824857: no support for stringifier attributes yet. // stringifier attribute USVString href; diff --git a/dom/webidl/MimeTypeArray.webidl b/dom/webidl/MimeTypeArray.webidl index 0187f8413f..21bfc6dc0d 100644 --- a/dom/webidl/MimeTypeArray.webidl +++ b/dom/webidl/MimeTypeArray.webidl @@ -4,6 +4,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ +// [LegacyUnenumerableNamedProperties] +// Named properties enumerable for now; see +// https://bugzilla.mozilla.org/show_bug.cgi?id=1270364 interface MimeTypeArray { readonly attribute unsigned long length; diff --git a/dom/webidl/NamedNodeMap.webidl b/dom/webidl/NamedNodeMap.webidl index 82a8730195..41fbfcbb43 100644 --- a/dom/webidl/NamedNodeMap.webidl +++ b/dom/webidl/NamedNodeMap.webidl @@ -3,6 +3,7 @@ * 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/. */ +[LegacyUnenumerableNamedProperties] interface NamedNodeMap { getter Attr? getNamedItem(DOMString name); [Throws, BinaryName="setNamedItemNS"] diff --git a/dom/webidl/Plugin.webidl b/dom/webidl/Plugin.webidl index 0af7c4d6af..018d65a05c 100644 --- a/dom/webidl/Plugin.webidl +++ b/dom/webidl/Plugin.webidl @@ -4,6 +4,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ +// [LegacyUnenumerableNamedProperties] +// Named properties enumerable for now; see +// https://bugzilla.mozilla.org/show_bug.cgi?id=1270366 interface Plugin { readonly attribute DOMString description; readonly attribute DOMString filename; diff --git a/dom/webidl/PluginArray.webidl b/dom/webidl/PluginArray.webidl index 27bd318df7..617bb1f9a6 100644 --- a/dom/webidl/PluginArray.webidl +++ b/dom/webidl/PluginArray.webidl @@ -4,6 +4,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ +// [LegacyUnenumerableNamedProperties] +// Named properties enumerable for now; see +// https://bugzilla.mozilla.org/show_bug.cgi?id=1270366 interface PluginArray { readonly attribute unsigned long length; diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl index 9e89f951c9..d591787a93 100644 --- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -24,7 +24,7 @@ interface nsIDOMCrypto; typedef any Transferable; // http://www.whatwg.org/specs/web-apps/current-work/ -[PrimaryGlobal, NeedResolve] +[PrimaryGlobal, LegacyUnenumerableNamedProperties, NeedResolve] /*sealed*/ interface Window : EventTarget { // the current browsing context [Unforgeable, Constant, StoreInSlot, diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 5e5caa1191..410f7b912a 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -267,6 +267,7 @@ WEBIDL_FILES = [ 'IDBRequest.webidl', 'IDBTransaction.webidl', 'IDBVersionChangeEvent.webidl', + 'IIRFilterNode.webidl', 'ImageBitmap.webidl', 'ImageBitmapRenderingContext.webidl', 'ImageCapture.webidl', diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index d39000e19a..0887afb938 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -716,11 +716,9 @@ private: if (aStatus >= Terminating && !mCanceled) { mCanceled = true; - nsCOMPtr runnable = - NS_NewRunnableMethod(this, - &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted); - NS_ASSERTION(runnable, "This should never fail!"); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewRunnableMethod(this, + &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted))); } return true; diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index f79105c00a..5d86cd1f30 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -853,9 +853,8 @@ public: mColumn = column; } - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &WaitUntilHandler::ReportOnMainThread); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget())); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewRunnableMethod(this, &WaitUntilHandler::ReportOnMainThread))); } void diff --git a/dom/workers/ServiceWorkerJob.cpp b/dom/workers/ServiceWorkerJob.cpp index c634fb9154..73716fab61 100644 --- a/dom/workers/ServiceWorkerJob.cpp +++ b/dom/workers/ServiceWorkerJob.cpp @@ -95,7 +95,7 @@ ServiceWorkerJob::Start(Callback* aFinalCallback) mState = State::Started; nsCOMPtr runnable = - NS_NewRunnableMethod(this, &ServiceWorkerJob::AsyncExecute); + NewRunnableMethod(this, &ServiceWorkerJob::AsyncExecute); // We may have to wait for the PBackground actor to be initialized // before proceeding. We should always be able to get a ServiceWorkerManager, diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index 3ada307db7..ba13cf17ef 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -622,7 +622,7 @@ public: return; } nsCOMPtr runnable = - NS_NewRunnableMethodWithArg(this, + NewRunnableMethod(this, &PushErrorReporter::ReportOnMainThread, aReason); MOZ_ALWAYS_TRUE(NS_SUCCEEDED( NS_DispatchToMainThread(runnable.forget()))); @@ -1457,7 +1457,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, // if the ServiceWorker script fails to load for some reason, just resume // the original channel. nsCOMPtr failRunnable = - NS_NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception); + NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception); nsresult rv = SpawnWorkerIfNeeded(FetchEvent, failRunnable, aLoadGroup); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/workers/ServiceWorkerRegistrar.cpp b/dom/workers/ServiceWorkerRegistrar.cpp index a9609aae3d..54ab9b4d58 100644 --- a/dom/workers/ServiceWorkerRegistrar.cpp +++ b/dom/workers/ServiceWorkerRegistrar.cpp @@ -540,7 +540,7 @@ public: service->SaveData(); RefPtr runnable = - NS_NewRunnableMethod(service, &ServiceWorkerRegistrar::DataSaved); + NewRunnableMethod(service, &ServiceWorkerRegistrar::DataSaved); nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -614,7 +614,7 @@ ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted() } RefPtr runnable = - NS_NewRunnableMethod(this, &ServiceWorkerRegistrar::ShutdownCompleted); + NewRunnableMethod(this, &ServiceWorkerRegistrar::ShutdownCompleted); nsresult rv = NS_DispatchToMainThread(runnable); if (NS_WARN_IF(NS_FAILED(rv))) { return; @@ -744,7 +744,7 @@ ServiceWorkerRegistrar::ProfileStarted() MOZ_ASSERT(target, "Must have stream transport service"); nsCOMPtr runnable = - NS_NewRunnableMethod(this, &ServiceWorkerRegistrar::LoadData); + NewRunnableMethod(this, &ServiceWorkerRegistrar::LoadData); rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL); if (NS_FAILED(rv)) { NS_WARNING("Failed to dispatch the LoadDataRunnable."); diff --git a/dom/workers/ServiceWorkerRegistrationInfo.cpp b/dom/workers/ServiceWorkerRegistrationInfo.cpp index aeb96a4478..3bf6348e62 100644 --- a/dom/workers/ServiceWorkerRegistrationInfo.cpp +++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp @@ -201,10 +201,9 @@ ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId) void ServiceWorkerRegistrationInfo::TryToActivateAsync() { - nsCOMPtr r = - NS_NewRunnableMethod(this, - &ServiceWorkerRegistrationInfo::TryToActivate); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewRunnableMethod(this, + &ServiceWorkerRegistrationInfo::TryToActivate))); } /* @@ -236,14 +235,14 @@ ServiceWorkerRegistrationInfo::Activate() // "Queue a task to fire a simple event named controllerchange..." nsCOMPtr controllerChangeRunnable = - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( swm, &ServiceWorkerManager::FireControllerChange, this); NS_DispatchToMainThread(controllerChangeRunnable); nsCOMPtr failRunnable = - NS_NewRunnableMethodWithArg(this, - &ServiceWorkerRegistrationInfo::FinishActivate, - false /* success */); + NewRunnableMethod(this, + &ServiceWorkerRegistrationInfo::FinishActivate, + false /* success */); nsMainThreadPtrHandle handle( new nsMainThreadPtrHolder(this)); diff --git a/dom/workers/ServiceWorkerUpdateJob.cpp b/dom/workers/ServiceWorkerUpdateJob.cpp index d1f20a3452..337c3dca36 100644 --- a/dom/workers/ServiceWorkerUpdateJob.cpp +++ b/dom/workers/ServiceWorkerUpdateJob.cpp @@ -489,7 +489,7 @@ ServiceWorkerUpdateJob::Install() // fire the updatefound event nsCOMPtr upr = - NS_NewRunnableMethodWithArg>( + NewRunnableMethod>( swm, &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations, mRegistration); @@ -497,7 +497,7 @@ ServiceWorkerUpdateJob::Install() // Call ContinueAfterInstallEvent(false) on main thread if the SW // script fails to load. - nsCOMPtr failRunnable = NS_NewRunnableMethodWithArgs + nsCOMPtr failRunnable = NewRunnableMethod (this, &ServiceWorkerUpdateJob::ContinueAfterInstallEvent, false); nsMainThreadPtrHandle handle( diff --git a/dom/xbl/nsBindingManager.cpp b/dom/xbl/nsBindingManager.cpp index 9e2bb52771..b3ee9b121a 100644 --- a/dom/xbl/nsBindingManager.cpp +++ b/dom/xbl/nsBindingManager.cpp @@ -347,7 +347,7 @@ void nsBindingManager::PostProcessAttachedQueueEvent() { mProcessAttachedQueueEvent = - NS_NewRunnableMethod(this, &nsBindingManager::DoProcessAttachedQueue); + NewRunnableMethod(this, &nsBindingManager::DoProcessAttachedQueue); nsresult rv = NS_DispatchToCurrentThread(mProcessAttachedQueueEvent); if (NS_SUCCEEDED(rv) && mDocument) { mDocument->BlockOnload(); diff --git a/dom/xml/XMLStylesheetProcessingInstruction.cpp b/dom/xml/XMLStylesheetProcessingInstruction.cpp index cc5734c6a4..3d94e179b4 100644 --- a/dom/xml/XMLStylesheetProcessingInstruction.cpp +++ b/dom/xml/XMLStylesheetProcessingInstruction.cpp @@ -63,7 +63,7 @@ XMLStylesheetProcessingInstruction::BindToTree(nsIDocument* aDocument, void (XMLStylesheetProcessingInstruction::*update)() = &XMLStylesheetProcessingInstruction::UpdateStyleSheetInternal; - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update)); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, update)); return rv; } diff --git a/dom/xml/nsXMLContentSink.cpp b/dom/xml/nsXMLContentSink.cpp index 811ea755a6..9935b9ba5d 100644 --- a/dom/xml/nsXMLContentSink.cpp +++ b/dom/xml/nsXMLContentSink.cpp @@ -1558,7 +1558,7 @@ nsXMLContentSink::ContinueInterruptedParsingIfEnabled() void nsXMLContentSink::ContinueInterruptedParsingAsync() { - nsCOMPtr ev = NS_NewRunnableMethod(this, + nsCOMPtr ev = NewRunnableMethod(this, &nsXMLContentSink::ContinueInterruptedParsingIfEnabled); NS_DispatchToCurrentThread(ev); diff --git a/dom/xml/nsXMLPrettyPrinter.cpp b/dom/xml/nsXMLPrettyPrinter.cpp index ee50cd0ac3..2e086dde0b 100644 --- a/dom/xml/nsXMLPrettyPrinter.cpp +++ b/dom/xml/nsXMLPrettyPrinter.cpp @@ -194,7 +194,7 @@ nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent) // synchronously mUnhookPending = true; nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsXMLPrettyPrinter::Unhook)); + NewRunnableMethod(this, &nsXMLPrettyPrinter::Unhook)); } } diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp index 6e6262d912..50ba10c233 100644 --- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -1015,7 +1015,7 @@ XULDocument::AttributeChanged(nsIDocument* aDocument, if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() && // XXXldb This should check that it's a token, not just a substring. persist.Find(nsDependentAtomString(aAttribute)) >= 0) { - nsContentUtils::AddScriptRunner(NS_NewRunnableMethodWithArgs + nsContentUtils::AddScriptRunner(NewRunnableMethod (this, &XULDocument::DoPersist, aElement, kNameSpaceID_None, aAttribute)); @@ -3135,7 +3135,7 @@ XULDocument::MaybeBroadcast() if (!nsContentUtils::IsSafeToRunScript()) { if (!mInDestructor) { nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &XULDocument::MaybeBroadcast)); + NewRunnableMethod(this, &XULDocument::MaybeBroadcast)); } return; } diff --git a/dom/xul/templates/nsXULTemplateBuilder.cpp b/dom/xul/templates/nsXULTemplateBuilder.cpp index 632afcd064..2ae794944c 100644 --- a/dom/xul/templates/nsXULTemplateBuilder.cpp +++ b/dom/xul/templates/nsXULTemplateBuilder.cpp @@ -1096,13 +1096,13 @@ nsXULTemplateBuilder::AttributeChanged(nsIDocument* aDocument, // beneath the element. if (aAttribute == nsGkAtoms::ref) nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableRebuild)); + NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableRebuild)); // Check for a change to the 'datasources' attribute. If so, setup // mDB by parsing the new value and rebuild. else if (aAttribute == nsGkAtoms::datasources) { nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableLoadAndRebuild)); + NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableLoadAndRebuild)); } } } @@ -1122,7 +1122,7 @@ nsXULTemplateBuilder::ContentRemoved(nsIDocument* aDocument, // Pass false to Uninit since content is going away anyway nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitFalse)); + NewRunnableMethod(this, &nsXULTemplateBuilder::UninitFalse)); MOZ_ASSERT(aDocument == mObservedDocument); StopObserving(); @@ -1161,7 +1161,7 @@ nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode) mCompDB = nullptr; nsContentUtils::AddScriptRunner( - NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitTrue)); + NewRunnableMethod(this, &nsXULTemplateBuilder::UninitTrue)); } diff --git a/editor/libeditor/nsHTMLEditRules.cpp b/editor/libeditor/nsHTMLEditRules.cpp index 803d6710ca..55c0d77d72 100644 --- a/editor/libeditor/nsHTMLEditRules.cpp +++ b/editor/libeditor/nsHTMLEditRules.cpp @@ -8731,7 +8731,7 @@ nsHTMLEditRules::WillRelativeChangeZIndex(Selection* aSelection, NS_IMETHODIMP nsHTMLEditRules::DocumentModified() { - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, &nsHTMLEditRules::DocumentModifiedWorker)); + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, &nsHTMLEditRules::DocumentModifiedWorker)); return NS_OK; } diff --git a/editor/libeditor/nsHTMLEditor.cpp b/editor/libeditor/nsHTMLEditor.cpp index 569702e103..05e9325ebc 100644 --- a/editor/libeditor/nsHTMLEditor.cpp +++ b/editor/libeditor/nsHTMLEditor.cpp @@ -3227,7 +3227,7 @@ nsHTMLEditor::DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer, nsCOMPtr kungFuDeathGrip(this); if (ShouldReplaceRootElement()) { - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod( + nsContentUtils::AddScriptRunner(NewRunnableMethod( this, &nsHTMLEditor::ResetRootElementAndEventTarget)); } // We don't need to handle our own modifications @@ -3273,7 +3273,7 @@ nsHTMLEditor::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer, nsCOMPtr kungFuDeathGrip(this); if (SameCOMIdentity(aChild, mRootElement)) { - nsContentUtils::AddScriptRunner(NS_NewRunnableMethod( + nsContentUtils::AddScriptRunner(NewRunnableMethod( this, &nsHTMLEditor::ResetRootElementAndEventTarget)); } // We don't need to handle our own modifications diff --git a/editor/txmgr/nsTransactionItem.cpp b/editor/txmgr/nsTransactionItem.cpp index 5ca1b56d2c..3317ca755b 100644 --- a/editor/txmgr/nsTransactionItem.cpp +++ b/editor/txmgr/nsTransactionItem.cpp @@ -237,7 +237,7 @@ nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr) if (NS_SUCCEEDED(result)) { item = mUndoStack->Pop(); - mRedoStack->Push(item); + mRedoStack->Push(item.forget()); } nsresult result2 = aTxMgr->DidUndoNotify(t, result); @@ -310,7 +310,7 @@ nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr) if (NS_SUCCEEDED(result)) { item = mRedoStack->Pop(); - mUndoStack->Push(item); + mUndoStack->Push(item.forget()); } nsresult result2 = aTxMgr->DidUndoNotify(t, result); diff --git a/editor/txmgr/nsTransactionManager.cpp b/editor/txmgr/nsTransactionManager.cpp index a879db0b35..ee32e35eb9 100644 --- a/editor/txmgr/nsTransactionManager.cpp +++ b/editor/txmgr/nsTransactionManager.cpp @@ -102,15 +102,13 @@ nsTransactionManager::UndoTransaction() // executing a transaction's DoTransaction() method! If this happens, // the UndoTransaction() request is ignored, and we return NS_ERROR_FAILURE. - RefPtr tx = mDoStack.Peek(); - - if (tx) { + if (!mDoStack.IsEmpty()) { return NS_ERROR_FAILURE; } // Peek at the top of the undo stack. Don't remove the transaction // until it has successfully completed. - tx = mUndoStack.Peek(); + RefPtr tx = mUndoStack.Peek(); // Bail if there's nothing on the stack. if (!tx) { @@ -135,7 +133,7 @@ nsTransactionManager::UndoTransaction() if (NS_SUCCEEDED(result)) { tx = mUndoStack.Pop(); - mRedoStack.Push(tx); + mRedoStack.Push(tx.forget()); } nsresult result2 = DidUndoNotify(t, result); @@ -155,15 +153,13 @@ nsTransactionManager::RedoTransaction() // executing a transaction's DoTransaction() method! If this happens, // the RedoTransaction() request is ignored, and we return NS_ERROR_FAILURE. - RefPtr tx = mDoStack.Peek(); - - if (tx) { + if (!mDoStack.IsEmpty()) { return NS_ERROR_FAILURE; } // Peek at the top of the redo stack. Don't remove the transaction // until it has successfully completed. - tx = mRedoStack.Peek(); + RefPtr tx = mRedoStack.Peek(); // Bail if there's nothing on the stack. if (!tx) { @@ -188,7 +184,7 @@ nsTransactionManager::RedoTransaction() if (NS_SUCCEEDED(result)) { tx = mRedoStack.Pop(); - mUndoStack.Push(tx); + mUndoStack.Push(tx.forget()); } nsresult result2 = DidRedoNotify(t, result); @@ -331,9 +327,7 @@ nsTransactionManager::SetMaxTransactionCount(int32_t aMaxCount) // SetMaxTransactionCount() request is ignored, and we return // NS_ERROR_FAILURE. - RefPtr tx = mDoStack.Peek(); - - if (tx) { + if (!mDoStack.IsEmpty()) { return NS_ERROR_FAILURE; } @@ -364,7 +358,7 @@ nsTransactionManager::SetMaxTransactionCount(int32_t aMaxCount) // the bottom of the stack and pop towards the top. while (numUndoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) { - tx = mUndoStack.PopBottom(); + RefPtr tx = mUndoStack.PopBottom(); if (!tx) { return NS_ERROR_FAILURE; @@ -377,7 +371,7 @@ nsTransactionManager::SetMaxTransactionCount(int32_t aMaxCount) // the bottom of the stack and pop towards the top. while (numRedoItems > 0 && (numRedoItems + numUndoItems) > aMaxCount) { - tx = mRedoStack.PopBottom(); + RefPtr tx = mRedoStack.PopBottom(); if (!tx) { return NS_ERROR_FAILURE; @@ -487,15 +481,11 @@ nsTransactionManager::BatchTopUndo() nsresult nsTransactionManager::RemoveTopUndo() { - RefPtr lastUndo; - - lastUndo = mUndoStack.Peek(); - if (!lastUndo) { + if (mUndoStack.IsEmpty()) { return NS_OK; } - lastUndo = mUndoStack.Pop(); - + RefPtr lastUndo = mUndoStack.Pop(); return NS_OK; } @@ -898,7 +888,7 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty) // Push the transaction on the undo stack: - mUndoStack.Push(tx); + mUndoStack.Push(tx.forget()); return NS_OK; } diff --git a/editor/txmgr/nsTransactionStack.cpp b/editor/txmgr/nsTransactionStack.cpp index e75b7979ad..8cd23b5863 100644 --- a/editor/txmgr/nsTransactionStack.cpp +++ b/editor/txmgr/nsTransactionStack.cpp @@ -11,8 +11,16 @@ #include "nsTransactionStack.h" #include "nscore.h" -nsTransactionStack::nsTransactionStack(nsTransactionStack::Type aType) - : mType(aType) +class nsTransactionStackDeallocator : public nsDequeFunctor { + virtual void* operator() (void* aObject) { + RefPtr releaseMe = dont_AddRef(static_cast(aObject)); + return nullptr; + } +}; + +nsTransactionStack::nsTransactionStack(Type aType) + : nsDeque(new nsTransactionStackDeallocator()) + , mType(aType) { } @@ -22,72 +30,77 @@ nsTransactionStack::~nsTransactionStack() } void -nsTransactionStack::Push(nsTransactionItem *aTransaction) +nsTransactionStack::Push(nsTransactionItem* aTransactionItem) { - if (!aTransaction) { + if (!aTransactionItem) { return; } - // The stack's bottom is the front of the deque, and the top is the back. - mDeque.push_back(aTransaction); + RefPtr item(aTransactionItem); + Push(item.forget()); +} + +void +nsTransactionStack::Push(already_AddRefed aTransactionItem) +{ + RefPtr item(aTransactionItem); + if (!item) { + return; + } + + nsDeque::Push(item.forget().take()); } already_AddRefed nsTransactionStack::Pop() { - if (mDeque.empty()) { - return nullptr; - } - RefPtr ret = mDeque.back().forget(); - mDeque.pop_back(); - return ret.forget(); + RefPtr item = + dont_AddRef(static_cast(nsDeque::Pop())); + return item.forget(); } already_AddRefed nsTransactionStack::PopBottom() { - if (mDeque.empty()) { - return nullptr; - } - RefPtr ret = mDeque.front().forget(); - mDeque.pop_front(); - return ret.forget(); + RefPtr item = + dont_AddRef(static_cast(nsDeque::PopFront())); + return item.forget(); } already_AddRefed nsTransactionStack::Peek() { - if (mDeque.empty()) { - return nullptr; - } - RefPtr ret = mDeque.back(); - return ret.forget(); + RefPtr item = + static_cast(nsDeque::Peek()); + return item.forget(); } already_AddRefed nsTransactionStack::GetItem(int32_t aIndex) { - if (aIndex < 0 || aIndex >= static_cast(mDeque.size())) { + if (aIndex < 0 || aIndex >= static_cast(nsDeque::GetSize())) { return nullptr; } - RefPtr ret = mDeque[aIndex]; - return ret.forget(); + RefPtr item = + static_cast(nsDeque::ObjectAt(aIndex)); + return item.forget(); } void nsTransactionStack::Clear() { - while (!mDeque.empty()) { - RefPtr tx = mType == FOR_UNDO ? Pop() : PopBottom(); + while (GetSize() != 0) { + RefPtr item = + mType == FOR_UNDO ? Pop() : PopBottom(); } } void nsTransactionStack::DoTraverse(nsCycleCollectionTraversalCallback &cb) { - int32_t size = mDeque.size(); + int32_t size = GetSize(); for (int32_t i = 0; i < size; ++i) { - nsTransactionItem* item = mDeque[i]; + nsTransactionItem* item = static_cast(nsDeque::ObjectAt(i)); if (item) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "transaction stack mDeque[i]"); cb.NoteNativeChild(item, NS_CYCLE_COLLECTION_PARTICIPANT(nsTransactionItem)); diff --git a/editor/txmgr/nsTransactionStack.h b/editor/txmgr/nsTransactionStack.h index 2c56280954..7b211808cb 100644 --- a/editor/txmgr/nsTransactionStack.h +++ b/editor/txmgr/nsTransactionStack.h @@ -6,13 +6,13 @@ #ifndef nsTransactionStack_h__ #define nsTransactionStack_h__ -#include +#include "nsDeque.h" #include "nsAutoPtr.h" class nsCycleCollectionTraversalCallback; class nsTransactionItem; -class nsTransactionStack +class nsTransactionStack : private nsDeque { public: enum Type { FOR_UNDO, FOR_REDO }; @@ -21,18 +21,19 @@ public: ~nsTransactionStack(); void Push(nsTransactionItem *aTransactionItem); + void Push(already_AddRefed aTransactionItem); already_AddRefed Pop(); already_AddRefed PopBottom(); already_AddRefed Peek(); already_AddRefed GetItem(int32_t aIndex); void Clear(); - int32_t GetSize() { return mDeque.size(); } + int32_t GetSize() const { return static_cast(nsDeque::GetSize()); } + bool IsEmpty() const { return GetSize() == 0; } void DoUnlink() { Clear(); } void DoTraverse(nsCycleCollectionTraversalCallback &cb); private: - std::deque > mDeque; const Type mType; }; diff --git a/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp index 35a6291875..248b05b9b0 100644 --- a/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp +++ b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp @@ -43,7 +43,7 @@ WebBrowserPersistDocumentParent::ActorDestroy(ActorDestroyReason aWhy) // dropping the last reference to another document's // WebBrowserPersistRemoteDocument. To avoid that, defer the // callback until after the entire subtree is destroyed. - nsCOMPtr errorLater = NS_NewRunnableMethodWithArg + nsCOMPtr errorLater = NewRunnableMethod (mOnReady, &nsIWebBrowserPersistDocumentReceiver::OnError, NS_ERROR_FAILURE); NS_DispatchToCurrentThread(errorLater); diff --git a/embedding/components/webbrowserpersist/WebBrowserPersistResourcesParent.cpp b/embedding/components/webbrowserpersist/WebBrowserPersistResourcesParent.cpp index 13c1f7f65e..195cabb58b 100644 --- a/embedding/components/webbrowserpersist/WebBrowserPersistResourcesParent.cpp +++ b/embedding/components/webbrowserpersist/WebBrowserPersistResourcesParent.cpp @@ -33,7 +33,7 @@ WebBrowserPersistResourcesParent::ActorDestroy(ActorDestroyReason aWhy) if (aWhy != Deletion && mVisitor) { // See comment in WebBrowserPersistDocumentParent::ActorDestroy // (or bug 1202887) for why this is deferred. - nsCOMPtr errorLater = NS_NewRunnableMethodWithArgs + nsCOMPtr errorLater = NewRunnableMethod , nsresult> (mVisitor, &nsIWebBrowserPersistResourceVisitor::EndVisit, mDocument, NS_ERROR_FAILURE); diff --git a/embedding/components/webbrowserpersist/WebBrowserPersistSerializeParent.cpp b/embedding/components/webbrowserpersist/WebBrowserPersistSerializeParent.cpp index 348ba3418a..2f76ffb332 100644 --- a/embedding/components/webbrowserpersist/WebBrowserPersistSerializeParent.cpp +++ b/embedding/components/webbrowserpersist/WebBrowserPersistSerializeParent.cpp @@ -77,7 +77,7 @@ WebBrowserPersistSerializeParent::ActorDestroy(ActorDestroyReason aWhy) MOZ_ASSERT(aWhy != Deletion); // See comment in WebBrowserPersistDocumentParent::ActorDestroy // (or bug 1202887) for why this is deferred. - nsCOMPtr errorLater = NS_NewRunnableMethodWithArgs + nsCOMPtr errorLater = NewRunnableMethod , nsCOMPtr, nsCString, nsresult> (mFinish, &nsIWebBrowserPersistWriteCompletion::OnFinish, diff --git a/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp b/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp index 4f02d5959a..ed05b49d9c 100644 --- a/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp +++ b/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp @@ -672,7 +672,7 @@ nsWebBrowserPersist::SerializeNextFile() // Finish and clean things up. Defer this because the caller // may have been expecting to use the listeners that that // method will clear. - NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, + NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsWebBrowserPersist::FinishDownload)); return; } @@ -784,7 +784,7 @@ nsWebBrowserPersist::OnWrite::OnFinish(nsIWebBrowserPersistDocument* aDoc, return NS_OK; } } - NS_DispatchToCurrentThread(NS_NewRunnableMethod(mParent, + NS_DispatchToCurrentThread(NewRunnableMethod(mParent, &nsWebBrowserPersist::SerializeNextFile)); return NS_OK; } @@ -1796,8 +1796,8 @@ nsWebBrowserPersist::FinishSaveDocumentInternal(nsIURI* aFile, typedef StoreCopyPassByRRef WalkStorage; auto saveMethod = &nsWebBrowserPersist::SaveDocumentDeferred; nsCOMPtr saveLater = - NS_NewRunnableMethodWithArg(this, saveMethod, - mozilla::Move(toWalk)); + NewRunnableMethod(this, saveMethod, + mozilla::Move(toWalk)); NS_DispatchToCurrentThread(saveLater); } else { // Done walking DOMs; on to the serialization phase. diff --git a/gfx/gl/AndroidSurfaceTexture.cpp b/gfx/gl/AndroidSurfaceTexture.cpp index 08d880c7ca..02aa2fc957 100644 --- a/gfx/gl/AndroidSurfaceTexture.cpp +++ b/gfx/gl/AndroidSurfaceTexture.cpp @@ -293,8 +293,7 @@ AndroidSurfaceTexture::NotifyFrameAvailable() // Proxy to main thread if we aren't on it if (!NS_IsMainThread()) { // Proxy to main thread - nsCOMPtr event = NS_NewRunnableMethod(this, &AndroidSurfaceTexture::NotifyFrameAvailable); - NS_DispatchToCurrentThread(event); + NS_DispatchToCurrentThread(NewRunnableMethod(this, &AndroidSurfaceTexture::NotifyFrameAvailable)); } else { mFrameAvailableCallback->Run(); } diff --git a/gfx/ipc/CompositorSession.cpp b/gfx/ipc/CompositorSession.cpp new file mode 100644 index 0000000000..a12f9ceee0 --- /dev/null +++ b/gfx/ipc/CompositorSession.cpp @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "CompositorSession.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "base/process_util.h" + +namespace mozilla { +namespace layers { + +class InProcessCompositorSession final : public CompositorSession +{ +public: + InProcessCompositorSession( + widget::CompositorWidgetProxy* aWidgetProxy, + ClientLayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + int aSurfaceWidth, int aSurfaceHeight); + + CompositorBridgeParent* GetInProcessBridge() const override; + void SetContentController(GeckoContentController* aController) override; + uint64_t RootLayerTreeId() const override; + APZCTreeManager* GetAPZCTreeManager() const override; + void Shutdown() override; + +private: + RefPtr mCompositorBridgeParent; +}; + +already_AddRefed +CompositorSession::CreateTopLevel(widget::CompositorWidgetProxy* aWidgetProxy, + ClientLayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + int aSurfaceWidth, int aSurfaceHeight) +{ + RefPtr session = new InProcessCompositorSession( + aWidgetProxy, + aLayerManager, + aScale, + aUseAPZ, + aUseExternalSurfaceSize, + aSurfaceWidth, aSurfaceHeight); + return session.forget(); +} + +CompositorSession::CompositorSession() +{ +} + +CompositorSession::~CompositorSession() +{ +} + +CompositorBridgeChild* +CompositorSession::GetCompositorBridgeChild() +{ + return mCompositorBridgeChild; +} + +InProcessCompositorSession::InProcessCompositorSession(widget::CompositorWidgetProxy* aWidgetProxy, + ClientLayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + int aSurfaceWidth, int aSurfaceHeight) +{ + mCompositorBridgeParent = new CompositorBridgeParent( + aWidgetProxy, + aScale, + aUseAPZ, + aUseExternalSurfaceSize, + aSurfaceWidth, + aSurfaceHeight); + mCompositorBridgeChild = new CompositorBridgeChild(aLayerManager); + mCompositorBridgeChild->OpenSameProcess(mCompositorBridgeParent); + mCompositorBridgeParent->SetOtherProcessId(base::GetCurrentProcId()); +} + +CompositorBridgeParent* +InProcessCompositorSession::GetInProcessBridge() const +{ + return mCompositorBridgeParent; +} + +void +InProcessCompositorSession::SetContentController(GeckoContentController* aController) +{ + mCompositorBridgeParent->SetControllerForLayerTree(RootLayerTreeId(), aController); +} + +uint64_t +InProcessCompositorSession::RootLayerTreeId() const +{ + return mCompositorBridgeParent->RootLayerTreeId(); +} + +APZCTreeManager* +InProcessCompositorSession::GetAPZCTreeManager() const +{ + return mCompositorBridgeParent->GetAPZCTreeManager(RootLayerTreeId()); +} + +void +InProcessCompositorSession::Shutdown() +{ + // XXX CompositorBridgeChild and CompositorBridgeParent might be re-created in + // ClientLayerManager destructor. See bug 1133426. + RefPtr compositorChild = mCompositorBridgeChild; + RefPtr compositorParent = mCompositorBridgeParent; + mCompositorBridgeChild->Destroy(); + mCompositorBridgeChild = nullptr; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/CompositorSession.h b/gfx/ipc/CompositorSession.h new file mode 100644 index 0000000000..0e203349b0 --- /dev/null +++ b/gfx/ipc/CompositorSession.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 _include_mozilla_gfx_ipc_CompositorSession_h_ +#define _include_mozilla_gfx_ipc_CompositorSession_h_ + +#include "base/basictypes.h" +#include "Units.h" +#include "nsISupportsImpl.h" + +namespace mozilla { +namespace widget { +class CompositorWidgetProxy; +} // namespace widget +namespace layers { + +class GeckoContentController; +class APZCTreeManager; +class CompositorBridgeParent; +class CompositorBridgeChild; +class ClientLayerManager; + +// A CompositorSession provides access to a compositor without exposing whether +// or not it's in-process or out-of-process. +class CompositorSession +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorSession) + + static already_AddRefed CreateTopLevel( + widget::CompositorWidgetProxy* aWidgetProxy, + ClientLayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + int aSurfaceWidth, int aSurfaceHeight); + + virtual void Shutdown() = 0; + + // This returns a CompositorBridgeParent if the compositor resides in the same process. + virtual CompositorBridgeParent* GetInProcessBridge() const = 0; + + // Set the GeckoContentController for the root of the layer tree. + virtual void SetContentController(GeckoContentController* aController) = 0; + + // Return the id of the root layer tree. + virtual uint64_t RootLayerTreeId() const = 0; + + // Return the Async Pan/Zoom Tree Manager for this compositor. + virtual APZCTreeManager* GetAPZCTreeManager() const = 0; + + // Return the child end of the compositor IPC bridge. + CompositorBridgeChild* GetCompositorBridgeChild(); + +protected: + CompositorSession(); + virtual ~CompositorSession(); + +protected: + RefPtr mCompositorBridgeChild; + +private: + DISALLOW_COPY_AND_ASSIGN(CompositorSession); +}; + +} // namespace layers +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_CompositorSession_h_ diff --git a/gfx/ipc/moz.build b/gfx/ipc/moz.build index e3500a5b63..4598dcba47 100644 --- a/gfx/ipc/moz.build +++ b/gfx/ipc/moz.build @@ -13,6 +13,10 @@ EXPORTS.mozilla.gfx += [ 'SharedDIB.h', ] +EXPORTS.mozilla.layers += [ + 'CompositorSession.h', +] + if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': EXPORTS.mozilla.gfx += [ 'SharedDIBSurface.h', @@ -24,6 +28,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': ] UNIFIED_SOURCES += [ + 'CompositorSession.cpp', 'D3DMessageUtils.cpp', 'SharedDIB.cpp', ] diff --git a/gfx/layers/AtomicRefCountedWithFinalize.h b/gfx/layers/AtomicRefCountedWithFinalize.h index f6476106c6..793ab67b8c 100644 --- a/gfx/layers/AtomicRefCountedWithFinalize.h +++ b/gfx/layers/AtomicRefCountedWithFinalize.h @@ -68,9 +68,6 @@ public: template friend struct mozilla::RefPtrTraits; - template - friend struct ::RunnableMethodTraits; - template friend class ::mozilla::gl::RefSet; diff --git a/gfx/layers/Compositor.cpp b/gfx/layers/Compositor.cpp index 56ecb704f0..8969b04e97 100644 --- a/gfx/layers/Compositor.cpp +++ b/gfx/layers/Compositor.cpp @@ -7,6 +7,7 @@ #include "base/message_loop.h" // for MessageLoop #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent #include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc +#include "mozilla/layers/CompositorThread.h" #include "mozilla/mozalloc.h" // for operator delete, etc #include "gfx2DGlue.h" #include "nsAppRunner.h" @@ -25,8 +26,8 @@ namespace layers { /* static */ void Compositor::AssertOnCompositorThread() { - MOZ_ASSERT(!CompositorBridgeParent::CompositorLoop() || - CompositorBridgeParent::CompositorLoop() == MessageLoop::current(), + MOZ_ASSERT(!CompositorThreadHolder::Loop() || + CompositorThreadHolder::Loop() == MessageLoop::current(), "Can only call this from the compositor thread!"); } @@ -412,7 +413,7 @@ Compositor::IsValid() const #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 void -Compositor::SetDispAcquireFence(Layer* aLayer, nsIWidget* aWidget) +Compositor::SetDispAcquireFence(Layer* aLayer) { // OpenGL does not provide ReleaseFence for rendering. // Instead use DispAcquireFence as layer buffer's ReleaseFence @@ -420,11 +421,10 @@ Compositor::SetDispAcquireFence(Layer* aLayer, nsIWidget* aWidget) // DispAcquireFence is DisplaySurface's AcquireFence. // AcquireFence will be signaled when a buffer's content is available. // See Bug 974152. - - if (!aLayer || !aWidget) { + if (!aLayer || !mWidget) { return; } - nsWindow* window = static_cast(aWidget); + nsWindow* window = static_cast(mWidget->RealWidget()); RefPtr fence = new FenceHandle::FdObj( window->GetScreen()->GetPrevDispAcquireFd()); mReleaseFenceHandle.Merge(FenceHandle(fence)); @@ -443,7 +443,7 @@ Compositor::GetReleaseFence() #else void -Compositor::SetDispAcquireFence(Layer* aLayer, nsIWidget* aWidget) +Compositor::SetDispAcquireFence(Layer* aLayer) { } diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index 8f033fcc17..574473eba6 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -389,7 +389,7 @@ public: */ virtual void EndFrame() = 0; - virtual void SetDispAcquireFence(Layer* aLayer, nsIWidget* aWidget); + virtual void SetDispAcquireFence(Layer* aLayer); virtual FenceHandle GetReleaseFence(); @@ -533,6 +533,9 @@ public: // frames and should not be used. void SetInvalid(); bool IsValid() const; + CompositorBridgeParent* GetCompositorBridgeParent() const { + return mParent; + } protected: void DrawDiagnosticsInternal(DiagnosticFlags aFlags, diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 45efbc5e4d..7680d572be 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -649,8 +649,8 @@ APZCTreeManager::FlushApzRepaints(uint64_t aLayersId) const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId); MOZ_ASSERT(state && state->mController); - NS_DispatchToMainThread(NS_NewRunnableMethod( - state->mController.get(), &GeckoContentController::NotifyFlushComplete)); + NS_DispatchToMainThread(NewRunnableMethod( + state->mController, &GeckoContentController::NotifyFlushComplete)); } nsEventStatus @@ -1345,8 +1345,7 @@ APZCTreeManager::ClearTree() // Ensure that no references to APZCs are alive in any lingering input // blocks. This breaks cycles from InputBlockState::mTargetApzc back to // the InputQueue. - APZThreadUtils::RunOnControllerThread(NewRunnableMethod( - mInputQueue.get(), &InputQueue::Clear)); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod(mInputQueue, &InputQueue::Clear)); MutexAutoLock lock(mTreeLock); diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 885a35298f..9f358aae11 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -516,9 +516,9 @@ public: // is this one, then the SetState(NOTHING) in UpdateAnimation will // stomp on the SetState(SNAP_BACK) it does. mDeferredTasks.AppendElement( - NewRunnableMethod(mOverscrollHandoffChain.get(), - &OverscrollHandoffChain::SnapBackOverscrolledApzc, - &mApzc)); + NewRunnableMethod(mOverscrollHandoffChain.get(), + &OverscrollHandoffChain::SnapBackOverscrolledApzc, + &mApzc)); return false; } @@ -568,11 +568,13 @@ public: // called after mMonitor is released. APZC_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str()); mDeferredTasks.AppendElement( - NewRunnableMethod(&mApzc, - &AsyncPanZoomController::HandleFlingOverscroll, - velocity, - mOverscrollHandoffChain, - mScrolledApzc)); + NewRunnableMethod, + RefPtr>(&mApzc, + &AsyncPanZoomController::HandleFlingOverscroll, + velocity, + mOverscrollHandoffChain, + mScrolledApzc)); // If there is a remaining velocity on this APZC, continue this fling // as well. (This fling and the handed-off fling will run concurrently.) @@ -700,9 +702,7 @@ public: // The scroll snapping is done in a deferred task, otherwise the state // change to NOTHING caused by the overscroll animation ending would // clobber a possible state change to SMOOTH_SCROLL in ScrollSnap(). - mDeferredTasks.AppendElement( - NewRunnableMethod(&mApzc, - &AsyncPanZoomController::ScrollSnap)); + mDeferredTasks.AppendElement(NewRunnableMethod(&mApzc, &AsyncPanZoomController::ScrollSnap)); return false; } return true; @@ -819,9 +819,9 @@ public: // the lock ordering. Instead we schedule HandleSmoothScrollOverscroll() to be // called after mMonitor is released. mDeferredTasks.AppendElement( - NewRunnableMethod(&mApzc, - &AsyncPanZoomController::HandleSmoothScrollOverscroll, - velocity)); + NewRunnableMethod(&mApzc, + &AsyncPanZoomController::HandleSmoothScrollOverscroll, + velocity)); return false; } @@ -2180,11 +2180,14 @@ nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aP // the single tap message before the corresponding touch-up. To avoid that we // schedule the singletap message to run on the next spin of the event loop. // See bug 965381 for the issue this was causing. - controller->PostDelayedTask( - NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap, - geckoScreenPoint, aModifiers, - GetGuid()), - 0); + RefPtr runnable = + NewRunnableMethod(controller, &GeckoContentController::HandleSingleTap, + geckoScreenPoint, aModifiers, + GetGuid()); + + controller->PostDelayedTask(runnable.forget(), 0); return nsEventStatus_eConsumeNoDefault; } } @@ -2992,7 +2995,7 @@ void AsyncPanZoomController::RequestContentRepaint() { // use the local variable to resolve the function overload. auto func = static_cast (&AsyncPanZoomController::RequestContentRepaint); - NS_DispatchToMainThread(NS_NewRunnableMethod(this, func)); + NS_DispatchToMainThread(NewRunnableMethod(this, func)); return; } @@ -3725,7 +3728,7 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) { auto func = static_cast (&AsyncPanZoomController::RequestContentRepaint); NS_DispatchToMainThread( - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( this, func, endZoomToMetrics, velocity)); } } diff --git a/gfx/layers/apz/src/GestureEventListener.cpp b/gfx/layers/apz/src/GestureEventListener.cpp index a655dc0e35..b81ddc10ef 100644 --- a/gfx/layers/apz/src/GestureEventListener.cpp +++ b/gfx/layers/apz/src/GestureEventListener.cpp @@ -498,7 +498,7 @@ void GestureEventListener::CancelLongTapTimeoutTask() void GestureEventListener::CreateLongTapTimeoutTask() { RefPtr task = - NewRunnableMethod(this, &GestureEventListener::HandleInputTimeoutLongTap); + NewCancelableRunnableMethod(this, &GestureEventListener::HandleInputTimeoutLongTap); mLongTapTimeoutTask = task; mAsyncPanZoomController->PostDelayedTask( @@ -525,8 +525,9 @@ void GestureEventListener::CreateMaxTapTimeoutTask() TouchBlockState* block = mAsyncPanZoomController->GetInputQueue()->CurrentTouchBlock(); RefPtr task = - NewRunnableMethod(this, &GestureEventListener::HandleInputTimeoutMaxTap, - block->IsDuringFastFling()); + NewCancelableRunnableMethod(this, + &GestureEventListener::HandleInputTimeoutMaxTap, + block->IsDuringFastFling()); mMaxTapTimeoutTask = task; mAsyncPanZoomController->PostDelayedTask( diff --git a/gfx/layers/apz/src/InputQueue.cpp b/gfx/layers/apz/src/InputQueue.cpp index 0909465d6a..c267b097b2 100644 --- a/gfx/layers/apz/src/InputQueue.cpp +++ b/gfx/layers/apz/src/InputQueue.cpp @@ -570,9 +570,10 @@ InputQueue::ScheduleMainThreadTimeout(const RefPtr& aTar CancelableBlockState* aBlock) { INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get()); aBlock->StartContentResponseTimer(); - aTarget->PostDelayedTask( - NewRunnableMethod(this, &InputQueue::MainThreadTimeout, aBlock->GetBlockId()), - gfxPrefs::APZContentResponseTimeout()); + aTarget->PostDelayedTask(NewRunnableMethod(this, + &InputQueue::MainThreadTimeout, + aBlock->GetBlockId()), + gfxPrefs::APZContentResponseTimeout()); } void diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index dd857c7ecf..6775d67957 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -690,7 +690,7 @@ public: } APZCCH_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId); - SendLayersDependentApzcTargetConfirmation(mPresShell, mInputBlockId, mTargets); + SendLayersDependentApzcTargetConfirmation(mPresShell, mInputBlockId, Move(mTargets)); if (!mPresShell->RemovePostRefreshObserver(this)) { MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered"); @@ -721,7 +721,7 @@ SendSetTargetAPZCNotificationHelper(nsIWidget* aWidget, if (waitForRefresh) { APZCCH_LOG("At least one target got a new displayport, need to wait for refresh\n"); waitForRefresh = aShell->AddPostRefreshObserver( - new DisplayportSetListener(aShell, aInputBlockId, aTargets)); + new DisplayportSetListener(aShell, aInputBlockId, Move(aTargets))); } if (!waitForRefresh) { APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId); @@ -772,7 +772,7 @@ APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget, aWidget, shell, aInputBlockId, - targets, + Move(targets), waitForRefresh); } } @@ -792,7 +792,7 @@ APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification( widget::ContentHelper::GetAllowedTouchBehavior( aWidget, aEvent.mTouches[i]->mRefPoint)); } - aCallback(aInputBlockId, flags); + aCallback(aInputBlockId, Move(flags)); } void diff --git a/gfx/layers/apz/util/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h index c421957823..c9002c05e7 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.h +++ b/gfx/layers/apz/util/APZCCallbackHelper.h @@ -141,9 +141,9 @@ public: /* Figure out the allowed touch behaviors of each touch point in |aEvent| * and send that information to the provided callback. */ static void SendSetAllowedTouchBehaviorNotification(nsIWidget* aWidget, - const WidgetTouchEvent& aEvent, - uint64_t aInputBlockId, - const SetAllowedTouchBehaviorCallback& aCallback); + const WidgetTouchEvent& aEvent, + uint64_t aInputBlockId, + const SetAllowedTouchBehaviorCallback& aCallback); /* Notify content of a mouse scroll testing event. */ static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent); diff --git a/gfx/layers/apz/util/ActiveElementManager.cpp b/gfx/layers/apz/util/ActiveElementManager.cpp index 436fa0436c..20d34aa2bb 100644 --- a/gfx/layers/apz/util/ActiveElementManager.cpp +++ b/gfx/layers/apz/util/ActiveElementManager.cpp @@ -94,8 +94,10 @@ ActiveElementManager::TriggerElementActivation() // bug properly should make this unnecessary. MOZ_ASSERT(mSetActiveTask == nullptr); - RefPtr task = NewRunnableMethod( - this, &ActiveElementManager::SetActiveTask, mTarget); + RefPtr task = + NewCancelableRunnableMethod>(this, + &ActiveElementManager::SetActiveTask, + mTarget); mSetActiveTask = task; MessageLoop::current()->PostDelayedTask(task.forget(), sActivationDelayMs); AEM_LOG("Scheduling mSetActiveTask %p\n", mSetActiveTask); diff --git a/gfx/layers/apz/util/ChromeProcessController.cpp b/gfx/layers/apz/util/ChromeProcessController.cpp index ee7aab2375..19ec6ac269 100644 --- a/gfx/layers/apz/util/ChromeProcessController.cpp +++ b/gfx/layers/apz/util/ChromeProcessController.cpp @@ -36,8 +36,7 @@ ChromeProcessController::ChromeProcessController(nsIWidget* aWidget, MOZ_ASSERT(aAPZEventState); MOZ_ASSERT(aAPZCTreeManager); - mUILoop->PostTask( - NewRunnableMethod(this, &ChromeProcessController::InitializeRoot)); + mUILoop->PostTask(NewRunnableMethod(this, &ChromeProcessController::InitializeRoot)); } ChromeProcessController::~ChromeProcessController() {} @@ -71,8 +70,7 @@ void ChromeProcessController::Destroy() { if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - NewRunnableMethod(this, &ChromeProcessController::Destroy)); + mUILoop->PostTask(NewRunnableMethod(this, &ChromeProcessController::Destroy)); return; } @@ -121,9 +119,12 @@ ChromeProcessController::HandleDoubleTap(const mozilla::CSSPoint& aPoint, const ScrollableLayerGuid& aGuid) { if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - NewRunnableMethod(this, &ChromeProcessController::HandleDoubleTap, - aPoint, aModifiers, aGuid)); + mUILoop->PostTask(NewRunnableMethod + (this, + &ChromeProcessController::HandleDoubleTap, + aPoint, aModifiers, aGuid)); return; } @@ -158,9 +159,12 @@ ChromeProcessController::HandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid) { if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - NewRunnableMethod(this, &ChromeProcessController::HandleSingleTap, - aPoint, aModifiers, aGuid)); + mUILoop->PostTask(NewRunnableMethod + (this, + &ChromeProcessController::HandleSingleTap, + aPoint, aModifiers, aGuid)); return; } @@ -173,9 +177,12 @@ ChromeProcessController::HandleLongTap(const mozilla::CSSPoint& aPoint, Modifier uint64_t aInputBlockId) { if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - NewRunnableMethod(this, &ChromeProcessController::HandleLongTap, - aPoint, aModifiers, aGuid, aInputBlockId)); + mUILoop->PostTask(NewRunnableMethod + (this, &ChromeProcessController::HandleLongTap, + aPoint, aModifiers, aGuid, aInputBlockId)); return; } @@ -189,9 +196,11 @@ ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, int aArg) { if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - NewRunnableMethod(this, &ChromeProcessController::NotifyAPZStateChange, - aGuid, aChange, aArg)); + mUILoop->PostTask(NewRunnableMethod + (this, &ChromeProcessController::NotifyAPZStateChange, + aGuid, aChange, aArg)); return; } @@ -202,8 +211,10 @@ void ChromeProcessController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent) { if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - NewRunnableMethod(this, &ChromeProcessController::NotifyMozMouseScrollEvent, aScrollId, aEvent)); + mUILoop->PostTask(NewRunnableMethod + (this, &ChromeProcessController::NotifyMozMouseScrollEvent, + aScrollId, aEvent)); return; } diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp index a49450e015..c6c0de84ad 100644 --- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -68,6 +68,7 @@ #endif #include "GeckoProfiler.h" #include "TextRenderer.h" // for TextRenderer +#include "mozilla/layers/CompositorBridgeParent.h" class gfxContext; @@ -944,7 +945,7 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegi mCompositor->EndFrame(); // Call after EndFrame() - mCompositor->SetDispAcquireFence(mRoot, mCompositor->GetWidget()->RealWidget()); + mCompositor->SetDispAcquireFence(mRoot); } if (composer2D) { @@ -1547,7 +1548,10 @@ LayerComposite::SetLayerManager(LayerManagerComposite* aManager) bool LayerManagerComposite::AsyncPanZoomEnabled() const { - return mCompositor->GetWidget()->RealWidget()->AsyncPanZoomEnabled(); + if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) { + return bridge->AsyncPanZoomEnabled(); + } + return false; } nsIntRegion diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index c552cf1e14..0351d5b1a4 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -11,6 +11,7 @@ #include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory #include "mozilla/ipc/Shmem.h" // for Shmem #include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager +#include "mozilla/layers/CompositorBridgeParent.h" #include "mozilla/layers/Compositor.h" // for Compositor #include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc @@ -64,7 +65,7 @@ namespace layers { class TextureParent : public ParentActor { public: - explicit TextureParent(CompositableParentManager* aManager); + explicit TextureParent(ISurfaceAllocator* aAllocator); ~TextureParent(); @@ -82,26 +83,26 @@ public: virtual void Destroy() override; - CompositableParentManager* mCompositableManager; + ISurfaceAllocator* mSurfaceAllocator; RefPtr mWaitForClientRecycle; RefPtr mTextureHost; }; //////////////////////////////////////////////////////////////////////////////// PTextureParent* -TextureHost::CreateIPDLActor(CompositableParentManager* aManager, +TextureHost::CreateIPDLActor(ISurfaceAllocator* aAllocator, const SurfaceDescriptor& aSharedData, LayersBackend aLayersBackend, TextureFlags aFlags) { if (aSharedData.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer && aSharedData.get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::Tuintptr_t && - !aManager->IsSameProcess()) + !aAllocator->IsSameProcess()) { NS_ERROR("A client process is trying to peek at our address space using a MemoryTexture!"); return nullptr; } - TextureParent* actor = new TextureParent(aManager); + TextureParent* actor = new TextureParent(aAllocator); if (!actor->Init(aSharedData, aLayersBackend, aFlags)) { delete actor; return nullptr; @@ -891,8 +892,8 @@ size_t MemoryTextureHost::GetBufferSize() return std::numeric_limits::max(); } -TextureParent::TextureParent(CompositableParentManager* aCompositableManager) -: mCompositableManager(aCompositableManager) +TextureParent::TextureParent(ISurfaceAllocator* aSurfaceAllocator) +: mSurfaceAllocator(aSurfaceAllocator) { MOZ_COUNT_CTOR(TextureParent); } @@ -942,7 +943,7 @@ TextureParent::Init(const SurfaceDescriptor& aSharedData, const TextureFlags& aFlags) { mTextureHost = TextureHost::Create(aSharedData, - mCompositableManager, + mSurfaceAllocator, aBackend, aFlags); if (mTextureHost) { diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h index ff43084203..80dd8eed0d 100644 --- a/gfx/layers/composite/TextureHost.h +++ b/gfx/layers/composite/TextureHost.h @@ -41,6 +41,7 @@ namespace layers { class BufferDescriptor; class Compositor; class CompositableParentManager; +class CompositorBridgeParent; class SurfaceDescriptor; class ISurfaceAllocator; class TextureHostOGL; @@ -478,7 +479,7 @@ public: * are for use with the managing IPDL protocols only (so that they can * implement AllocPTextureParent and DeallocPTextureParent). */ - static PTextureParent* CreateIPDLActor(CompositableParentManager* aManager, + static PTextureParent* CreateIPDLActor(ISurfaceAllocator* aAllocator, const SurfaceDescriptor& aSharedData, LayersBackend aLayersBackend, TextureFlags aFlags); diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp index a9c4c922c0..af15685959 100644 --- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -22,6 +22,7 @@ #include "gfxVR.h" #include "mozilla/gfx/StackArray.h" #include "mozilla/Services.h" +#include "mozilla/widget/WinCompositorWidgetProxy.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/Telemetry.h" @@ -215,7 +216,7 @@ CompositorD3D11::Initialize() mFeatureLevel = mDevice->GetFeatureLevel(); - mHwnd = (HWND)mWidget->RealWidget()->GetNativeData(NS_NATIVE_WINDOW); + mHwnd = mWidget->AsWindowsProxy()->GetHwnd(); memset(&mVSConstants, 0, sizeof(VertexShaderConstants)); diff --git a/gfx/layers/d3d9/CompositorD3D9.cpp b/gfx/layers/d3d9/CompositorD3D9.cpp index 6977b0c99a..f8be57f216 100644 --- a/gfx/layers/d3d9/CompositorD3D9.cpp +++ b/gfx/layers/d3d9/CompositorD3D9.cpp @@ -17,6 +17,7 @@ #include "gfxPrefs.h" #include "gfxCrashReporterUtils.h" #include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/widget/WinCompositorWidgetProxy.h" namespace mozilla { namespace layers { @@ -48,9 +49,7 @@ CompositorD3D9::Initialize() return false; } - mSwapChain = mDeviceManager-> - CreateSwapChain((HWND)mWidget->RealWidget()->GetNativeData(NS_NATIVE_WINDOW)); - + mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindowsProxy()->GetHwnd()); if (!mSwapChain) { return false; } @@ -593,8 +592,7 @@ CompositorD3D9::EnsureSwapChain() MOZ_ASSERT(mDeviceManager, "Don't call EnsureSwapChain without a device manager"); if (!mSwapChain) { - mSwapChain = mDeviceManager-> - CreateSwapChain((HWND)mWidget->RealWidget()->GetNativeData(NS_NATIVE_WINDOW)); + mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindowsProxy()->GetHwnd()); // We could not create a swap chain, return false if (!mSwapChain) { // Check the state of the device too diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp index 7d71907ecc..b845728fe7 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.cpp +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -6,6 +6,7 @@ #include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" #include // for size_t #include "ClientLayerManager.h" // for ClientLayerManager #include "base/message_loop.h" // for MessageLoop @@ -112,6 +113,16 @@ CompositorBridgeChild::Destroy() // From now on we can't send any message message. MessageLoop::current()->PostTask( NewRunnableFunction(DeferredDestroyCompositor, mCompositorBridgeParent, selfRef)); + + const ManagedContainer& textures = ManagedPTextureChild(); + for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) { + RefPtr texture = TextureClient::AsTextureClient(iter.Get()->GetKey()); + + if (texture) { + texture->Destroy(); + } + } + } // static @@ -170,7 +181,7 @@ CompositorBridgeChild::OpenSameProcess(CompositorBridgeParent* aParent) mCompositorBridgeParent = aParent; mCanSend = Open(mCompositorBridgeParent->GetIPCChannel(), - CompositorBridgeParent::CompositorLoop(), + CompositorThreadHolder::Loop(), ipc::ChildSide); return mCanSend; } @@ -750,6 +761,21 @@ CompositorBridgeChild::SendUpdateVisibleRegion(VisibilityCounter aCounter, return PCompositorBridgeChild::SendUpdateVisibleRegion(aCounter, aGuid, aRegion); } +PTextureChild* +CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, + const LayersBackend&, + const TextureFlags&, + const uint64_t&) +{ + return TextureClient::CreateIPDLActor(); +} + +bool +CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor) +{ + return TextureClient::DestroyIPDLActor(actor); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h index 0087aca420..7531772c95 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.h +++ b/gfx/layers/ipc/CompositorBridgeChild.h @@ -96,6 +96,13 @@ public: virtual bool RecvHideAllPlugins(const uintptr_t& aParentWidget) override; + virtual PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId) override; + + virtual bool DeallocPTextureChild(PTextureChild* actor) override; + /** * Request that the parent tell us when graphics are ready on GPU. * When we get that message, we bounce it to the TabParent via diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp index 8433079de4..752ba38133 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -37,6 +37,7 @@ #include "mozilla/layers/Compositor.h" // for Compositor #include "mozilla/layers/CompositorLRU.h" // for CompositorLRU #include "mozilla/layers/CompositorOGL.h" // for CompositorOGL +#include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/CompositorTypes.h" #include "mozilla/layers/FrameUniformityData.h" #include "mozilla/layers/ImageBridgeParent.h" @@ -89,11 +90,6 @@ namespace mozilla { -namespace gfx { -// See VRManagerChild.cpp -void ReleaseVRManagerParentSingleton(); -} // namespace gfx - namespace layers { using namespace mozilla::ipc; @@ -154,99 +150,30 @@ CompositorBridgeParent::ForEachIndirectLayerTree(const Lambda& aCallback) * compositor */ typedef map CompositorMap; -static CompositorMap* sCompositorMap; +static StaticAutoPtr sCompositorMap; -static void CreateCompositorMap() +void +CompositorBridgeParent::Initialize() { + EnsureLayerTreeMapReady(); + MOZ_ASSERT(!sCompositorMap); sCompositorMap = new CompositorMap; } -static void DestroyCompositorMap() +void +CompositorBridgeParent::Shutdown() { MOZ_ASSERT(sCompositorMap); MOZ_ASSERT(sCompositorMap->empty()); - delete sCompositorMap; sCompositorMap = nullptr; } -// See ImageBridgeChild.cpp -void ReleaseImageBridgeParentSingleton(); - -CompositorThreadHolder::CompositorThreadHolder() - : mCompositorThread(CreateCompositorThread()) +void +CompositorBridgeParent::FinishShutdown() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_COUNT_CTOR(CompositorThreadHolder); -} - -CompositorThreadHolder::~CompositorThreadHolder() -{ - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_COUNT_DTOR(CompositorThreadHolder); - - DestroyCompositorThread(mCompositorThread); -} - -static StaticRefPtr sCompositorThreadHolder; -static bool sFinishedCompositorShutDown = false; - -CompositorThreadHolder* GetCompositorThreadHolder() -{ - return sCompositorThreadHolder; -} - -/* static */ Thread* -CompositorThreadHolder::CreateCompositorThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); - - Thread* compositorThread = new Thread("Compositor"); - - Thread::Options options; - /* Timeout values are powers-of-two to enable us get better data. - 128ms is chosen for transient hangs because 8Hz should be the minimally - acceptable goal for Compositor responsiveness (normal goal is 60Hz). */ - options.transient_hang_timeout = 128; // milliseconds - /* 2048ms is chosen for permanent hangs because it's longer than most - * Compositor hangs seen in the wild, but is short enough to not miss getting - * native hang stacks. */ - options.permanent_hang_timeout = 2048; // milliseconds -#if defined(_WIN32) - /* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As - * such the thread is a gui thread, and must process a windows message queue or - * risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */ - options.message_loop_type = MessageLoop::TYPE_UI; -#endif - - if (!compositorThread->StartWithOptions(options)) { - delete compositorThread; - return nullptr; - } - - EnsureLayerTreeMapReady(); - CreateCompositorMap(); - - return compositorThread; -} - -/* static */ void -CompositorThreadHolder::DestroyCompositorThread(Thread* aCompositorThread) -{ - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet."); - - DestroyCompositorMap(); - delete aCompositorThread; - sFinishedCompositorShutDown = true; -} - -static Thread* CompositorThread() { - return sCompositorThreadHolder ? sCompositorThreadHolder->GetCompositorThread() : nullptr; + // TODO: this should be empty by now... + sIndirectLayerTrees.clear(); } static void SetThreadPriority() @@ -305,12 +232,14 @@ CompositorVsyncScheduler::Observer::Destroy() mOwner = nullptr; } -CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent, nsIWidget* aWidget) +CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent, + widget::CompositorWidgetProxy* aWidgetProxy) : mCompositorBridgeParent(aCompositorBridgeParent) , mLastCompose(TimeStamp::Now()) , mIsObservingVsync(false) , mNeedsComposite(0) , mVsyncNotificationsSkipped(0) + , mCompositorVsyncDispatcher(aWidgetProxy->GetCompositorVsyncDispatcher()) , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor") , mCurrentCompositeTask(nullptr) , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor") @@ -324,9 +253,7 @@ CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aComp #endif { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aWidget != nullptr); mVsyncObserver = new Observer(this); - mCompositorVsyncDispatcher = aWidget->GetCompositorVsyncDispatcher(); #ifdef MOZ_WIDGET_GONK GeckoTouchDispatcher::GetInstance()->SetCompositorVsyncScheduler(this); @@ -348,7 +275,6 @@ CompositorVsyncScheduler::~CompositorVsyncScheduler() MOZ_ASSERT(!mVsyncObserver); // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners mCompositorBridgeParent = nullptr; - mCompositorVsyncDispatcher = nullptr; } #ifdef MOZ_WIDGET_GONK @@ -362,7 +288,7 @@ CompositorVsyncScheduler::SetDisplay(bool aDisplayEnable) MOZ_ASSERT(NS_IsMainThread()); MonitorAutoLock lock(mSetDisplayMonitor); RefPtr task = - NewRunnableMethod(this, &CompositorVsyncScheduler::SetDisplay, aDisplayEnable); + NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::SetDisplay, aDisplayEnable); mSetDisplayTask = task; ScheduleTask(task.forget(), 0); return; @@ -427,8 +353,8 @@ CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp) MonitorAutoLock lock(mCurrentCompositeTaskMonitor); if (mCurrentCompositeTask == nullptr) { RefPtr task = - NewRunnableMethod(this, &CompositorVsyncScheduler::Composite, - aCompositeTimestamp); + NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::Composite, + aCompositeTimestamp); mCurrentCompositeTask = task; ScheduleTask(task.forget(), 0); } @@ -481,7 +407,7 @@ CompositorVsyncScheduler::SetNeedsComposite() if (!CompositorBridgeParent::IsInCompositorThread()) { MonitorAutoLock lock(mSetNeedsCompositeMonitor); RefPtr task = - NewRunnableMethod(this, &CompositorVsyncScheduler::SetNeedsComposite); + NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::SetNeedsComposite); mSetNeedsCompositeTask = task; ScheduleTask(task.forget(), 0); return; @@ -625,47 +551,13 @@ CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) vm->NotifyVsync(aVsyncTimestamp); } -void CompositorBridgeParent::StartUp() -{ - MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); - MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); - - sCompositorThreadHolder = new CompositorThreadHolder(); -} - -void CompositorBridgeParent::ShutDown() -{ - MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); - MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!"); - - ReleaseImageBridgeParentSingleton(); - ReleaseVRManagerParentSingleton(); - MediaSystemResourceService::Shutdown(); - - sCompositorThreadHolder = nullptr; - - // No locking is needed around sFinishedCompositorShutDown because it is only - // ever accessed on the main thread. - while (!sFinishedCompositorShutDown) { - NS_ProcessNextEvent(nullptr, true); - } - - // TODO: this should be empty by now... - sIndirectLayerTrees.clear(); -} - -MessageLoop* CompositorBridgeParent::CompositorLoop() -{ - return CompositorThread() ? CompositorThread()->message_loop() : nullptr; -} - void CompositorVsyncScheduler::ScheduleTask(already_AddRefed aTask, int aTime) { - MOZ_ASSERT(CompositorBridgeParent::CompositorLoop()); + MOZ_ASSERT(CompositorThreadHolder::Loop()); MOZ_ASSERT(aTime >= 0); - CompositorBridgeParent::CompositorLoop()->PostDelayedTask(Move(aTask), aTime); + CompositorThreadHolder::Loop()->PostDelayedTask(Move(aTask), aTime); } void @@ -684,6 +576,12 @@ CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRec mCompositorBridgeParent->CompositeToTarget(aTarget, aRect); } +static inline MessageLoop* +CompositorLoop() +{ + return CompositorThreadHolder::Loop(); +} + CompositorBridgeParent::CompositorBridgeParent(widget::CompositorWidgetProxy* aWidget, CSSToLayoutDeviceScale aScale, bool aUseAPZ, @@ -701,7 +599,7 @@ CompositorBridgeParent::CompositorBridgeParent(widget::CompositorWidgetProxy* aW , mRootLayerTreeID(AllocateLayerTreeId()) , mOverrideComposeReadiness(false) , mForceCompositionTask(nullptr) - , mCompositorThreadHolder(sCompositorThreadHolder) + , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) , mCompositorScheduler(nullptr) #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) , mLastPluginUpdateLayerTreeId(0) @@ -713,6 +611,10 @@ CompositorBridgeParent::CompositorBridgeParent(widget::CompositorWidgetProxy* aW MOZ_ASSERT(CompositorThread(), "The compositor thread must be Initialized before instanciating a CompositorBridgeParent."); MOZ_COUNT_CTOR(CompositorBridgeParent); + + // Always run destructor on the main thread + SetMessageLoopToPostDestructionTo(MessageLoop::current()); + mCompositorID = 0; // FIXME: This holds on the the fact that right now the only thing that // can destroy this instance is initialized on the compositor thread after @@ -733,7 +635,7 @@ CompositorBridgeParent::CompositorBridgeParent(widget::CompositorWidgetProxy* aW mApzcTreeManager = new APZCTreeManager(); } - mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget->RealWidget()); + mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget); LayerScope::SetPixelScale(aScale.scale); // mSelfRef is cleared in DeferredDestroy. @@ -756,6 +658,15 @@ CompositorBridgeParent::~CompositorBridgeParent() { MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_DTOR(CompositorBridgeParent); + + InfallibleTArray textures; + ManagedPTextureParent(textures); + // We expect all textures to be destroyed by now. + MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0); + for (unsigned int i = 0; i < textures.Length(); ++i) { + RefPtr tex = TextureHost::AsTextureHost(textures[i]); + tex->DeallocateDeviceData(); + } } void @@ -981,7 +892,7 @@ CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) // We must keep the compositor parent alive untill the code handling message // reception is finished on this thread. mSelfRef = this; - MessageLoop::current()->PostTask(NewRunnableMethod(this,&CompositorBridgeParent::DeferredDestroy)); + MessageLoop::current()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::DeferredDestroy)); } @@ -1113,7 +1024,10 @@ CompositorBridgeParent::ScheduleResumeOnCompositorThread(int width, int height) MonitorAutoLock lock(mResumeCompositionMonitor); MOZ_ASSERT(CompositorLoop()); - CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ResumeCompositionAndResize, width, height)); + CompositorLoop()->PostTask(NewRunnableMethod + (this, + &CompositorBridgeParent::ResumeCompositionAndResize, + width, height)); // Wait until the resume has actually been processed by the compositor thread lock.Wait(); @@ -1239,8 +1153,7 @@ CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRec // to local pages, hide every plugin associated with the window. if (!hasRemoteContent && BrowserTabsRemoteAutostart() && mCachedPluginData.Length()) { - nsIWidget* widget = GetWidgetProxy()->RealWidget(); - Unused << SendHideAllPlugins(reinterpret_cast(widget)); + Unused << SendHideAllPlugins(GetWidgetProxy()->GetWidgetKey()); mCachedPluginData.Clear(); } #endif @@ -1365,7 +1278,8 @@ CompositorBridgeParent::ScheduleRotationOnCompositorThread(const TargetConfig& a if (mForceCompositionTask != nullptr) { mForceCompositionTask->Cancel(); } - RefPtr task = NewRunnableMethod(this, &CompositorBridgeParent::ForceComposition); + RefPtr task = + NewCancelableRunnableMethod(this, &CompositorBridgeParent::ForceComposition); mForceCompositionTask = task; ScheduleTask(task.forget(), gfxPrefs::OrientationSyncMillis()); } @@ -1865,7 +1779,7 @@ InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) CompositorBridgeParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) { // Called in the vsync thread - if (profiler_is_active() && sCompositorThreadHolder) { + if (profiler_is_active() && CompositorThreadHolder::IsActive()) { CompositorLoop()->PostTask( NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp)); } @@ -1895,11 +1809,12 @@ CompositorBridgeParent::RequestNotifyLayerTreeCleared(uint64_t aLayersId, Compos * hands off work to the CompositorBridgeParent it's associated with. */ class CrossProcessCompositorBridgeParent final : public PCompositorBridgeParent, - public ShadowLayersManager + public ShadowLayersManager, + public ISurfaceAllocator, + public ShmemAllocator { friend class CompositorBridgeParent; - NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CrossProcessCompositorBridgeParent) public: explicit CrossProcessCompositorBridgeParent(Transport* aTransport) : mTransport(aTransport) @@ -1908,6 +1823,8 @@ public: , mDestroyCalled(false) { MOZ_ASSERT(NS_IsMainThread()); + // Always run destructor on the main thread + SetMessageLoopToPostDestructionTo(MessageLoop::current()); } // IToplevelProtocol::CloneToplevel() @@ -2030,8 +1947,31 @@ public: TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd); + virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId) override; + + virtual bool DeallocPTextureParent(PTextureParent* actor) override; + + virtual bool IsSameProcess() const override; + + virtual ShmemAllocator* AsShmemAllocator() override { return this; } + + virtual bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) override; + + virtual bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) override; + + virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + protected: - void OnChannelConnected(int32_t pid) override { mCompositorThreadHolder = sCompositorThreadHolder; } + void OnChannelConnected(int32_t pid) override { + mCompositorThreadHolder = CompositorThreadHolder::GetSingleton(); + } private: // Private destructor, to discourage deletion outside of Release(): virtual ~CrossProcessCompositorBridgeParent(); @@ -2106,11 +2046,12 @@ CompositorBridgeParent::ResetCompositor(const nsTArray& aBackendH { MonitorAutoLock lock(mResetCompositorMonitor); - CompositorLoop()->PostTask( - NewRunnableMethod(this, - &CompositorBridgeParent::ResetCompositorTask, - aBackendHints, - &newIdentifier)); + CompositorLoop()->PostTask(NewRunnableMethod + >, + Maybe*>(this, + &CompositorBridgeParent::ResetCompositorTask, + aBackendHints, + &newIdentifier)); mResetCompositorMonitor.Wait(); } @@ -2242,6 +2183,49 @@ CompositorBridgeParent::GetIndirectShadowTree(uint64_t aId) return &cit->second; } +PTextureParent* +CompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId) +{ + return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags); +} + +bool +CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) +{ + return TextureHost::DestroyIPDLActor(actor); +} + +bool +CompositorBridgeParent::AllocShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem); +} + +bool +CompositorBridgeParent::AllocUnsafeShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem); +} + +void +CompositorBridgeParent::DeallocShmem(ipc::Shmem& aShmem) +{ + PCompositorBridgeParent::DeallocShmem(aShmem); +} + +bool +CompositorBridgeParent::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + bool CrossProcessCompositorBridgeParent::RecvNotifyHidden(const uint64_t& id) { @@ -2278,8 +2262,7 @@ CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy) // We must keep this object alive untill the code handling message // reception is finished on this thread. - MessageLoop::current()->PostTask( - NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy)); + MessageLoop::current()->PostTask(NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy)); } PLayerTransactionParent* @@ -2469,8 +2452,7 @@ CompositorBridgeParent::UpdatePluginWindowState(uint64_t aId) return false; } - nsIWidget* widget = GetWidgetProxy()->RealWidget(); - uintptr_t parentWidget = reinterpret_cast(widget); + uintptr_t parentWidget = GetWidgetProxy()->GetWidgetKey(); // We will pass through here in cases where the previous shadow layer // tree contained visible plugins and the new tree does not. All we need @@ -2555,8 +2537,7 @@ CompositorBridgeParent::HideAllPluginWindows() return; } - nsIWidget* widget = GetWidgetProxy()->RealWidget(); - uintptr_t parentWidget = reinterpret_cast(widget); + uintptr_t parentWidget = GetWidgetProxy()->GetWidgetKey(); mDeferPluginWindows = true; mPluginWindowsHidden = true; @@ -2766,5 +2747,69 @@ CrossProcessCompositorBridgeParent::CloneToplevel( return nullptr; } +PTextureParent* +CrossProcessCompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId) +{ + CompositorBridgeParent::LayerTreeState* state = nullptr; + + LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() != itr) { + state = &itr->second; + } + + TextureFlags flags = aFlags; + + if (!state || state->mPendingCompositorUpdates) { + // The compositor was recreated, and we're receiving layers updates for a + // a layer manager that will soon be discarded or invalidated. We can't + // return null because this will mess up deserialization later and we'll + // kill the content process. Instead, we signal that the underlying + // TextureHost should not attempt to access the compositor. + flags |= TextureFlags::INVALID_COMPOSITOR; + } else if (state->mLayerManager && state->mLayerManager->GetCompositor() && + aLayersBackend != state->mLayerManager->GetCompositor()->GetBackendType()) { + gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong"; + } + + return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags); +} + +bool +CrossProcessCompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) +{ + return TextureHost::DestroyIPDLActor(actor); +} + +bool +CrossProcessCompositorBridgeParent::AllocShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem); +} + +bool +CrossProcessCompositorBridgeParent::AllocUnsafeShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem); +} + +void +CrossProcessCompositorBridgeParent::DeallocShmem(ipc::Shmem& aShmem) +{ + PCompositorBridgeParent::DeallocShmem(aShmem); +} + +bool +CrossProcessCompositorBridgeParent::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h index c355cc398a..151979aa6e 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.h +++ b/gfx/layers/ipc/CompositorBridgeParent.h @@ -17,9 +17,6 @@ #include // for uint64_t #include "Layers.h" // for Layer -#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS -#include "base/platform_thread.h" // for PlatformThreadId -#include "base/thread.h" // for Thread #include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 #include "mozilla/Attributes.h" // for override #include "mozilla/Maybe.h" @@ -29,7 +26,9 @@ #include "mozilla/dom/ipc/IdType.h" #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/SharedMemory.h" #include "mozilla/layers/GeckoContentController.h" +#include "mozilla/layers/ISurfaceAllocator.h" // for ShmemAllocator #include "mozilla/layers/LayersMessages.h" // for TargetConfig #include "mozilla/layers/PCompositorBridgeParent.h" #include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager @@ -53,6 +52,7 @@ class DrawTarget; namespace ipc { class GeckoChildProcessHost; +class Shmem; } // namespace ipc namespace layers { @@ -65,6 +65,7 @@ class LayerManagerComposite; class LayerTransactionParent; class PAPZParent; class CrossProcessCompositorBridgeParent; +class CompositorThreadHolder; struct ScopedLayerTreeRegistration { @@ -78,28 +79,6 @@ private: uint64_t mLayersId; }; -class CompositorThreadHolder final -{ - NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorThreadHolder) - -public: - CompositorThreadHolder(); - - base::Thread* GetCompositorThread() const { - return mCompositorThread; - } - -private: - ~CompositorThreadHolder(); - - base::Thread* const mCompositorThread; - - static base::Thread* CreateCompositorThread(); - static void DestroyCompositorThread(base::Thread* aCompositorThread); - - friend class CompositorBridgeParent; -}; - /** * Manages the vsync (de)registration and tracking on behalf of the * compositor when it need to paint. @@ -110,7 +89,8 @@ class CompositorVsyncScheduler NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler) public: - explicit CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent, nsIWidget* aWidget); + explicit CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent, + widget::CompositorWidgetProxy* aWidgetProxy); #ifdef MOZ_WIDGET_GONK // emulator-ics never trigger the display on/off, so compositor will always @@ -222,10 +202,12 @@ protected: }; class CompositorBridgeParent final : public PCompositorBridgeParent, - public ShadowLayersManager + public ShadowLayersManager, + public ISurfaceAllocator, + public ShmemAllocator { - NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorBridgeParent) friend class CompositorVsyncScheduler; + friend class CompositorThreadHolder; public: explicit CompositorBridgeParent(widget::CompositorWidgetProxy* aWidget, @@ -299,6 +281,26 @@ public: const nsTArray& aTargets) override; virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) override { return mCompositionManager; } + virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId) override; + virtual bool DeallocPTextureParent(PTextureParent* actor) override; + + virtual bool IsSameProcess() const override; + + virtual ShmemAllocator* AsShmemAllocator() override { return this; } + + virtual bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) override; + + virtual bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) override; + + virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + /** * Request that the compositor be recreated due to a shared device reset. * This must be called on the main thread, and blocks until a task posted @@ -370,27 +372,6 @@ public: */ static CompositorBridgeParent* GetCompositor(uint64_t id); - /** - * Returns the compositor thread's message loop. - * - * This message loop is used by CompositorBridgeParent, ImageBridgeParent, - * and VRManagerParent - */ - static MessageLoop* CompositorLoop(); - - /** - * Creates the compositor thread and the global compositor map. - */ - static void StartUp(); - - /** - * Waits for all [CrossProcess]CompositorBridgeParent's to be gone, - * and destroys the compositor thread and global compositor map. - * - * Does not return until all of that has completed. - */ - static void ShutDown(); - /** * Allocate an ID that can be used to refer to a layer tree and * associated resources that live only on the compositor thread. @@ -516,6 +497,9 @@ public: dom::ContentParent* aContentParent, const dom::TabId& aTabId, dom::TabParent* aBrowserParent); + bool AsyncPanZoomEnabled() const { + return !!mApzcTreeManager; + } protected: // Protected destructor, to discourage deletion outside of Release(): @@ -556,7 +540,22 @@ protected: */ static CompositorBridgeParent* RemoveCompositor(uint64_t id); - /** + /** + * Creates the global compositor map. + */ + static void Initialize(); + + /** + * Destroys the compositor thread and global compositor map. + */ + static void Shutdown(); + + /** + * Finish the shutdown operation on the compositor thread. + */ + static void FinishShutdown(); + + /** * Return true if current state allows compositing, that is * finishing a layers transaction. */ diff --git a/gfx/layers/ipc/CompositorThread.cpp b/gfx/layers/ipc/CompositorThread.cpp new file mode 100644 index 0000000000..969ad9f691 --- /dev/null +++ b/gfx/layers/ipc/CompositorThread.cpp @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 sts=2 ts=8 et tw=99 : */ +/* 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 "CompositorThread.h" +#include "MainThreadUtils.h" +#include "nsThreadUtils.h" +#include "CompositorBridgeParent.h" +#include "mozilla/media/MediaSystemResourceService.h" + +namespace mozilla { + +namespace gfx { +// See VRManagerChild.cpp +void ReleaseVRManagerParentSingleton(); +} // namespace gfx + +namespace layers { + +static StaticRefPtr sCompositorThreadHolder; +static bool sFinishedCompositorShutDown = false; + +// See ImageBridgeChild.cpp +void ReleaseImageBridgeParentSingleton(); + +CompositorThreadHolder* GetCompositorThreadHolder() +{ + return sCompositorThreadHolder; +} + +base::Thread* +CompositorThread() +{ + return sCompositorThreadHolder + ? sCompositorThreadHolder->GetCompositorThread() + : nullptr; +} + +/* static */ MessageLoop* +CompositorThreadHolder::Loop() +{ + return CompositorThread() ? CompositorThread()->message_loop() : nullptr; +} + +CompositorThreadHolder* +CompositorThreadHolder::GetSingleton() +{ + return sCompositorThreadHolder; +} + +CompositorThreadHolder::CompositorThreadHolder() + : mCompositorThread(CreateCompositorThread()) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_COUNT_CTOR(CompositorThreadHolder); +} + +CompositorThreadHolder::~CompositorThreadHolder() +{ + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_COUNT_DTOR(CompositorThreadHolder); + + DestroyCompositorThread(mCompositorThread); +} + +/* static */ void +CompositorThreadHolder::DestroyCompositorThread(base::Thread* aCompositorThread) +{ + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet."); + + CompositorBridgeParent::Shutdown(); + delete aCompositorThread; + sFinishedCompositorShutDown = true; +} + +/* static */ base::Thread* +CompositorThreadHolder::CreateCompositorThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); + + base::Thread* compositorThread = new base::Thread("Compositor"); + + base::Thread::Options options; + /* Timeout values are powers-of-two to enable us get better data. + 128ms is chosen for transient hangs because 8Hz should be the minimally + acceptable goal for Compositor responsiveness (normal goal is 60Hz). */ + options.transient_hang_timeout = 128; // milliseconds + /* 2048ms is chosen for permanent hangs because it's longer than most + * Compositor hangs seen in the wild, but is short enough to not miss getting + * native hang stacks. */ + options.permanent_hang_timeout = 2048; // milliseconds +#if defined(_WIN32) + /* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As + * such the thread is a gui thread, and must process a windows message queue or + * risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */ + options.message_loop_type = MessageLoop::TYPE_UI; +#endif + + if (!compositorThread->StartWithOptions(options)) { + delete compositorThread; + return nullptr; + } + + CompositorBridgeParent::Initialize(); + + return compositorThread; +} + +void +CompositorThreadHolder::Start() +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); + MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); + + sCompositorThreadHolder = new CompositorThreadHolder(); +} + +void +CompositorThreadHolder::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); + MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!"); + + ReleaseImageBridgeParentSingleton(); + gfx::ReleaseVRManagerParentSingleton(); + MediaSystemResourceService::Shutdown(); + + sCompositorThreadHolder = nullptr; + + // No locking is needed around sFinishedCompositorShutDown because it is only + // ever accessed on the main thread. + while (!sFinishedCompositorShutDown) { + NS_ProcessNextEvent(nullptr, true); + } + + CompositorBridgeParent::FinishShutdown(); +} + +} // namespace mozilla +} // namespace layers diff --git a/gfx/layers/ipc/CompositorThread.h b/gfx/layers/ipc/CompositorThread.h new file mode 100644 index 0000000000..7e0123a2ec --- /dev/null +++ b/gfx/layers/ipc/CompositorThread.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 sts=2 ts=8 et tw=99 : */ +/* 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 mozilla_layers_CompositorThread_h +#define mozilla_layers_CompositorThread_h + +#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS +#include "base/platform_thread.h" // for PlatformThreadId +#include "base/thread.h" // for Thread +#include "base/message_loop.h" +#include "nsISupportsImpl.h" +#include "ThreadSafeRefcountingWithMainThreadDestruction.h" + +namespace mozilla { +namespace layers { + +class CompositorThreadHolder final +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorThreadHolder) + +public: + CompositorThreadHolder(); + + base::Thread* GetCompositorThread() const { + return mCompositorThread; + } + + static CompositorThreadHolder* GetSingleton(); + + static bool IsActive() { + return !!GetSingleton(); + } + + /** + * Creates the compositor thread and the global compositor map. + */ + static void Start(); + + /* + * Waits for all [CrossProcess]CompositorBridgeParents to shutdown and + * releases compositor-thread owned resources. + */ + static void Shutdown(); + + static MessageLoop* Loop(); + +private: + ~CompositorThreadHolder(); + + base::Thread* const mCompositorThread; + + static base::Thread* CreateCompositorThread(); + static void DestroyCompositorThread(base::Thread* aCompositorThread); + + friend class CompositorBridgeParent; +}; + +base::Thread* CompositorThread(); + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CompositorThread_h diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp index eae77d0be3..89b834970d 100644 --- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -24,7 +24,7 @@ #include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager #include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild #include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc -#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent +#include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator #include "mozilla/layers/ImageClient.h" // for ImageClient #include "mozilla/layers/LayersMessages.h" // for CompositableOperation @@ -842,7 +842,7 @@ bool ImageBridgeChild::StartUpOnThread(Thread* aThread) } sImageBridgeChildSingleton = new ImageBridgeChild(); sImageBridgeParentSingleton = new ImageBridgeParent( - CompositorBridgeParent::CompositorLoop(), nullptr, base::GetCurrentProcId()); + CompositorThreadHolder::Loop(), nullptr, base::GetCurrentProcId()); sImageBridgeChildSingleton->ConnectAsync(sImageBridgeParentSingleton); sImageBridgeChildSingleton->GetMessageLoop()->PostTask( NewRunnableFunction(CallSendImageBridgeThreadId, diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp index a066389fb1..134337eecf 100644 --- a/gfx/layers/ipc/ImageBridgeParent.cpp +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -19,7 +19,6 @@ #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent #include "mozilla/layers/CompositableTransactionParent.h" -#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent #include "mozilla/layers/LayerManagerComposite.h" #include "mozilla/layers/LayersMessages.h" // for EditReply #include "mozilla/layers/LayersSurfaces.h" // for PGrallocBufferParent @@ -106,8 +105,7 @@ ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy) mSubprocess = nullptr; } - MessageLoop::current()->PostTask( - NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy)); + MessageLoop::current()->PostTask(NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy)); // It is very important that this method gets called at shutdown (be it a clean // or an abnormal shutdown), because DeferredDestroy is what clears mSelfRef. @@ -201,7 +199,7 @@ ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge, /*static*/ PImageBridgeParent* ImageBridgeParent::Create(Transport* aTransport, ProcessId aChildProcessId, GeckoChildProcessHost* aProcessHost) { - MessageLoop* loop = CompositorBridgeParent::CompositorLoop(); + MessageLoop* loop = CompositorThreadHolder::Loop(); RefPtr bridge = new ImageBridgeParent(loop, aTransport, aChildProcessId); if (aProcessHost) { diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h index b0f3c286a5..875aa93887 100644 --- a/gfx/layers/ipc/ImageBridgeParent.h +++ b/gfx/layers/ipc/ImageBridgeParent.h @@ -14,7 +14,7 @@ #include "mozilla/Attributes.h" // for override #include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc -#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/PImageBridgeParent.h" #include "nsAutoPtr.h" // for nsRefPtr #include "nsISupportsImpl.h" diff --git a/gfx/layers/ipc/LayerTransactionChild.cpp b/gfx/layers/ipc/LayerTransactionChild.cpp index ee2d3a3820..f197281dc4 100644 --- a/gfx/layers/ipc/LayerTransactionChild.cpp +++ b/gfx/layers/ipc/LayerTransactionChild.cpp @@ -37,15 +37,6 @@ LayerTransactionChild::Destroy() MOZ_ASSERT(0 == ManagedPLayerChild().Count(), "layers should have been cleaned up by now"); - const ManagedContainer& textures = ManagedPTextureChild(); - for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) { - RefPtr texture = TextureClient::AsTextureClient(iter.Get()->GetKey()); - - if (texture) { - texture->Destroy(); - } - } - SendShutdown(); } @@ -127,20 +118,5 @@ LayerTransactionChild::ActorDestroy(ActorDestroyReason why) #endif } -PTextureChild* -LayerTransactionChild::AllocPTextureChild(const SurfaceDescriptor&, - const LayersBackend&, - const TextureFlags&) -{ - MOZ_ASSERT(!mDestroyed); - return TextureClient::CreateIPDLActor(); -} - -bool -LayerTransactionChild::DeallocPTextureChild(PTextureChild* actor) -{ - return TextureClient::DestroyIPDLActor(actor); -} - } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/LayerTransactionChild.h b/gfx/layers/ipc/LayerTransactionChild.h index 511f6e9ac1..b3cedab2a4 100644 --- a/gfx/layers/ipc/LayerTransactionChild.h +++ b/gfx/layers/ipc/LayerTransactionChild.h @@ -65,11 +65,6 @@ protected: virtual PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo) override; virtual bool DeallocPCompositableChild(PCompositableChild* actor) override; - virtual PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData, - const LayersBackend& aLayersBackend, - const TextureFlags& aFlags) override; - virtual bool DeallocPTextureChild(PTextureChild* actor) override; - virtual bool RecvParentAsyncMessages(InfallibleTArray&& aMessages) override; diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index a448a8d4d3..404a06ed1e 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -177,14 +177,6 @@ LayerTransactionParent::Destroy() static_cast(iter.Get()->GetKey()); slp->Destroy(); } - InfallibleTArray textures; - ManagedPTextureParent(textures); - // We expect all textures to be destroyed by now. - MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0); - for (unsigned int i = 0; i < textures.Length(); ++i) { - RefPtr tex = TextureHost::AsTextureHost(textures[i]); - tex->DeallocateDeviceData(); - } mDestroyed = true; } @@ -960,33 +952,6 @@ LayerTransactionParent::DeallocPCompositableParent(PCompositableParent* aActor) return CompositableHost::DestroyIPDLActor(aActor); } -PTextureParent* -LayerTransactionParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, - const LayersBackend& aLayersBackend, - const TextureFlags& aFlags) -{ - TextureFlags flags = aFlags; - - if (mPendingCompositorUpdates) { - // The compositor was recreated, and we're receiving layers updates for a - // a layer manager that will soon be discarded or invalidated. We can't - // return null because this will mess up deserialization later and we'll - // kill the content process. Instead, we signal that the underlying - // TextureHost should not attempt to access the compositor. - flags |= TextureFlags::INVALID_COMPOSITOR; - } else if (aLayersBackend != mLayerManager->GetCompositor()->GetBackendType()) { - gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong"; - } - - return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, flags); -} - -bool -LayerTransactionParent::DeallocPTextureParent(PTextureParent* actor) -{ - return TextureHost::DestroyIPDLActor(actor); -} - bool LayerTransactionParent::RecvChildAsyncMessages(InfallibleTArray&& aMessages) { diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h index 353bf77743..ac2b76e033 100644 --- a/gfx/layers/ipc/LayerTransactionParent.h +++ b/gfx/layers/ipc/LayerTransactionParent.h @@ -159,11 +159,6 @@ protected: virtual PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo) override; virtual bool DeallocPCompositableParent(PCompositableParent* actor) override; - virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, - const LayersBackend& aLayersBackend, - const TextureFlags& aFlags) override; - virtual bool DeallocPTextureParent(PTextureParent* actor) override; - virtual bool RecvChildAsyncMessages(InfallibleTArray&& aMessages) override; diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl index ee18ea64a1..1fd80061f7 100644 --- a/gfx/layers/ipc/PCompositorBridge.ipdl +++ b/gfx/layers/ipc/PCompositorBridge.ipdl @@ -8,8 +8,11 @@ include LayersSurfaces; include LayersMessages; include protocol PBrowser; +include protocol PCompositable; +include protocol PImageContainer; include protocol PLayer; include protocol PLayerTransaction; +include protocol PTexture; include "mozilla/GfxMessageUtils.h"; using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; @@ -28,6 +31,7 @@ using mozilla::LayoutDeviceIntRegion from "Units.h"; using mozilla::VisibilityCounter from "VisibilityIPC.h"; using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h"; +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; namespace mozilla { namespace layers { @@ -42,6 +46,7 @@ sync protocol PCompositorBridge { // A Compositor manages a single Layer Manager (PLayerTransaction) manages PLayerTransaction; + manages PTexture; child: // The child should invalidate retained layers. This is used for local @@ -187,6 +192,8 @@ parent: ScrollableLayerGuid guid, CSSIntRegion region); + async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t id); + child: // Send back Compositor Frame Metrics from APZCs so tiled layers can // update progressively. diff --git a/gfx/layers/ipc/PLayerTransaction.ipdl b/gfx/layers/ipc/PLayerTransaction.ipdl index d99febd3a1..580f68be15 100644 --- a/gfx/layers/ipc/PLayerTransaction.ipdl +++ b/gfx/layers/ipc/PLayerTransaction.ipdl @@ -17,7 +17,6 @@ include "mozilla/GfxMessageUtils.h"; using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h"; using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; -using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h"; using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h"; using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; @@ -43,7 +42,6 @@ sync protocol PLayerTransaction { manager PCompositorBridge; manages PLayer; manages PCompositable; - manages PTexture; child: async ParentAsyncMessages(AsyncParentMessageData[] aMessages); @@ -51,7 +49,6 @@ child: parent: async PLayer(); async PCompositable(TextureInfo aTextureInfo); - async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags); // The isFirstPaint flag can be used to indicate that this is the first update // for a particular document. diff --git a/gfx/layers/ipc/PTexture.ipdl b/gfx/layers/ipc/PTexture.ipdl index b1b1893c21..a0ed795208 100644 --- a/gfx/layers/ipc/PTexture.ipdl +++ b/gfx/layers/ipc/PTexture.ipdl @@ -7,6 +7,7 @@ include LayersSurfaces; include protocol PLayerTransaction; +include protocol PCompositorBridge; include protocol PImageBridge; include "mozilla/GfxMessageUtils.h"; @@ -20,7 +21,7 @@ namespace layers { * PTexture is the IPDL glue between a TextureClient and a TextureHost. */ sync protocol PTexture { - manager PImageBridge or PLayerTransaction; + manager PImageBridge or PCompositorBridge; child: async __delete__(); diff --git a/gfx/layers/ipc/RemoteContentController.cpp b/gfx/layers/ipc/RemoteContentController.cpp index 180ca065e3..7756c6b741 100644 --- a/gfx/layers/ipc/RemoteContentController.cpp +++ b/gfx/layers/ipc/RemoteContentController.cpp @@ -58,9 +58,11 @@ RemoteContentController::HandleDoubleTap(const CSSPoint& aPoint, if (MessageLoop::current() != mUILoop) { // We have to send this message from the "UI thread" (main // thread). - mUILoop->PostTask( - NewRunnableMethod(this, &RemoteContentController::HandleDoubleTap, - aPoint, aModifiers, aGuid)); + mUILoop->PostTask(NewRunnableMethod(this, + &RemoteContentController::HandleDoubleTap, + aPoint, aModifiers, aGuid)); return; } if (CanSend()) { @@ -77,9 +79,11 @@ RemoteContentController::HandleSingleTap(const CSSPoint& aPoint, if (MessageLoop::current() != mUILoop) { // We have to send this message from the "UI thread" (main // thread). - mUILoop->PostTask( - NewRunnableMethod(this, &RemoteContentController::HandleSingleTap, - aPoint, aModifiers, aGuid)); + mUILoop->PostTask(NewRunnableMethod(this, + &RemoteContentController::HandleSingleTap, + aPoint, aModifiers, aGuid)); return; } @@ -110,9 +114,12 @@ RemoteContentController::HandleLongTap(const CSSPoint& aPoint, if (MessageLoop::current() != mUILoop) { // We have to send this message from the "UI thread" (main // thread). - mUILoop->PostTask( - NewRunnableMethod(this, &RemoteContentController::HandleLongTap, - aPoint, aModifiers, aGuid, aInputBlockId)); + mUILoop->PostTask(NewRunnableMethod(this, + &RemoteContentController::HandleLongTap, + aPoint, aModifiers, aGuid, aInputBlockId)); return; } if (CanSend()) { @@ -150,9 +157,11 @@ RemoteContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, int aArg) { if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - NewRunnableMethod(this, &RemoteContentController::NotifyAPZStateChange, - aGuid, aChange, aArg)); + mUILoop->PostTask(NewRunnableMethod(this, + &RemoteContentController::NotifyAPZStateChange, + aGuid, aChange, aArg)); return; } if (CanSend()) { @@ -165,9 +174,10 @@ RemoteContentController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& a const nsString& aEvent) { if (MessageLoop::current() != mUILoop) { - mUILoop->PostTask( - NewRunnableMethod(this, &RemoteContentController::NotifyMozMouseScrollEvent, - aScrollId, aEvent)); + mUILoop->PostTask(NewRunnableMethod(this, + &RemoteContentController::NotifyMozMouseScrollEvent, + aScrollId, aEvent)); return; } @@ -217,9 +227,11 @@ RemoteContentController::RecvContentReceivedInputBlock(const ScrollableLayerGuid return false; } if (RefPtr apzcTreeManager = GetApzcTreeManager()) { - APZThreadUtils::RunOnControllerThread(NewRunnableMethod( - apzcTreeManager.get(), &APZCTreeManager::ContentReceivedInputBlock, - aInputBlockId, aPreventDefault)); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod(apzcTreeManager, + &APZCTreeManager::ContentReceivedInputBlock, + aInputBlockId, aPreventDefault)); + } return true; } @@ -231,10 +243,11 @@ RemoteContentController::RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMet ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId, aDragMetrics.mViewId); - APZThreadUtils::RunOnControllerThread( - NewRunnableMethod(apzcTreeManager.get(), - &APZCTreeManager::StartScrollbarDrag, - guid, aDragMetrics)); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + (apzcTreeManager, + &APZCTreeManager::StartScrollbarDrag, + guid, aDragMetrics)); } return true; } @@ -254,9 +267,11 @@ RemoteContentController::RecvSetTargetAPZC(const uint64_t& aInputBlockId, // need a local var to disambiguate between the SetTargetAPZC overloads. void (APZCTreeManager::*setTargetApzcFunc)(uint64_t, const nsTArray&) = &APZCTreeManager::SetTargetAPZC; - APZThreadUtils::RunOnControllerThread(NewRunnableMethod( - apzcTreeManager.get(), setTargetApzcFunc, - aInputBlockId, aTargets)); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + >> + (apzcTreeManager, setTargetApzcFunc, aInputBlockId, aTargets)); + } return true; } @@ -266,9 +281,12 @@ RemoteContentController::RecvSetAllowedTouchBehavior(const uint64_t& aInputBlock nsTArray&& aFlags) { if (RefPtr apzcTreeManager = GetApzcTreeManager()) { - APZThreadUtils::RunOnControllerThread(NewRunnableMethod( - apzcTreeManager.get(), &APZCTreeManager::SetAllowedTouchBehavior, - aInputBlockId, Move(aFlags))); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + >> + (apzcTreeManager, + &APZCTreeManager::SetAllowedTouchBehavior, + aInputBlockId, Move(aFlags))); } return true; } diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 7f9d40faf0..a24d4eccab 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -21,6 +21,7 @@ #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/layers/CompositableClient.h" // for CompositableClient, etc +#include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/layers/LayersMessages.h" // for Edit, etc #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc @@ -1056,10 +1057,11 @@ ShadowLayerForwarder::CreateTexture(const SurfaceDescriptor& aSharedData, TextureFlags aFlags) { if (!HasShadowManager() || - !mShadowManager->IPCOpen()) { + !mShadowManager->IPCOpen() || + !mShadowManager->Manager()) { return nullptr; } - return mShadowManager->SendPTextureConstructor(aSharedData, aLayersBackend, aFlags); + return mShadowManager->Manager()->SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, mShadowManager->GetId()); } diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index ba7c537326..4d796bfcdd 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -158,6 +158,7 @@ EXPORTS.mozilla.layers += [ 'ipc/CompositorBridgeChild.h', 'ipc/CompositorBridgeParent.h', 'ipc/CompositorLRU.h', + 'ipc/CompositorThread.h', 'ipc/FenceUtils.h', 'ipc/GonkNativeHandle.h', 'ipc/GonkNativeHandleUtils.h', @@ -338,6 +339,7 @@ UNIFIED_SOURCES += [ 'ipc/CompositorBridgeChild.cpp', 'ipc/CompositorBridgeParent.cpp', 'ipc/CompositorLRU.cpp', + 'ipc/CompositorThread.cpp', 'ipc/FenceUtils.cpp', 'ipc/ImageBridgeChild.cpp', 'ipc/ImageBridgeParent.cpp', diff --git a/gfx/thebes/SoftwareVsyncSource.cpp b/gfx/thebes/SoftwareVsyncSource.cpp index ccc6b59a14..d86ff9bbd2 100644 --- a/gfx/thebes/SoftwareVsyncSource.cpp +++ b/gfx/thebes/SoftwareVsyncSource.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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/. @@ -9,6 +9,8 @@ #include "gfxPlatform.h" #include "nsThreadUtils.h" +using namespace mozilla; + SoftwareVsyncSource::SoftwareVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); @@ -128,14 +130,15 @@ SoftwareDisplay::ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp) nextVsync = mozilla::TimeStamp::Now(); } - mCurrentVsyncTask = NewRunnableMethod(this, - &SoftwareDisplay::NotifyVsync, - nextVsync); + mCurrentVsyncTask = + NewCancelableRunnableMethod(this, + &SoftwareDisplay::NotifyVsync, + nextVsync); - RefPtr addrefedTask = mCurrentVsyncTask; + RefPtr addrefedTask = mCurrentVsyncTask; mVsyncThread->message_loop()->PostDelayedTask( - addrefedTask.forget(), - delay.ToMilliseconds()); + addrefedTask.forget(), + delay.ToMilliseconds()); } void diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index ce6b322e38..952b58847e 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -5,7 +5,7 @@ #include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTracker #include "mozilla/layers/CompositorBridgeChild.h" -#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/SharedBufferManagerChild.h" #include "mozilla/layers/ISurfaceAllocator.h" // for GfxMemoryImageReporter @@ -862,7 +862,7 @@ gfxPlatform::InitLayersIPC() if (XRE_IsParentProcess()) { - mozilla::layers::CompositorBridgeParent::StartUp(); + layers::CompositorThreadHolder::Start(); #ifdef MOZ_WIDGET_GONK SharedBufferManagerChild::StartUp(); #endif @@ -899,7 +899,7 @@ gfxPlatform::ShutdownLayersIPC() #endif // This has to happen after shutting down the child protocols. - layers::CompositorBridgeParent::ShutDown(); + layers::CompositorThreadHolder::Shutdown(); } else { // TODO: There are other kind of processes and we should make sure gfx // stuff is either not created there or shut down properly. diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 606efc8174..705f013480 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -65,10 +65,10 @@ #include #include "d3dkmtQueryStatistics.h" +#include "base/thread.h" #include "SurfaceCache.h" #include "gfxPrefs.h" #include "gfxConfig.h" - #include "VsyncSource.h" #include "DriverCrashGuard.h" #include "gfxCrashReporterUtils.h" diff --git a/gfx/vr/ipc/VRManagerChild.cpp b/gfx/vr/ipc/VRManagerChild.cpp index c8476969de..cd4c21fb55 100644 --- a/gfx/vr/ipc/VRManagerChild.cpp +++ b/gfx/vr/ipc/VRManagerChild.cpp @@ -10,7 +10,7 @@ #include "VRDeviceProxy.h" #include "VRDeviceProxyOrientationFallBack.h" #include "mozilla/StaticPtr.h" -#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent +#include "mozilla/layers/CompositorThread.h" // for CompositorThread #include "mozilla/dom/Navigator.h" namespace mozilla { @@ -77,7 +77,7 @@ VRManagerChild::StartUpSameProcess() sVRManagerChildSingleton = new VRManagerChild(); sVRManagerParentSingleton = VRManagerParent::CreateSameProcess(); sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(), - mozilla::layers::CompositorBridgeParent::CompositorLoop(), + mozilla::layers::CompositorThreadHolder::Loop(), mozilla::ipc::ChildSide); } } diff --git a/gfx/vr/ipc/VRManagerParent.cpp b/gfx/vr/ipc/VRManagerParent.cpp index b68baa817d..91d43cc788 100644 --- a/gfx/vr/ipc/VRManagerParent.cpp +++ b/gfx/vr/ipc/VRManagerParent.cpp @@ -10,18 +10,11 @@ #include "mozilla/ipc/ProtocolTypes.h" #include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol #include "mozilla/TimeStamp.h" // for TimeStamp -#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" #include "mozilla/unused.h" #include "VRManager.h" namespace mozilla { -namespace layers { - -// defined in CompositorBridgeParent.cpp -CompositorThreadHolder* GetCompositorThreadHolder(); - -} // namespace layers - namespace gfx { VRManagerParent::VRManagerParent(MessageLoop* aLoop, @@ -76,7 +69,7 @@ VRManagerParent::ConnectVRManagerInParentProcess(VRManagerParent* aVRManager, /*static*/ VRManagerParent* VRManagerParent::CreateCrossProcess(Transport* aTransport, ProcessId aChildProcessId) { - MessageLoop* loop = mozilla::layers::CompositorBridgeParent::CompositorLoop(); + MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop(); RefPtr vmp = new VRManagerParent(loop, aTransport, aChildProcessId); vmp->mSelfRef = vmp; loop->PostTask(NewRunnableFunction(ConnectVRManagerInParentProcess, @@ -93,9 +86,9 @@ VRManagerParent::RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager /*static*/ VRManagerParent* VRManagerParent::CreateSameProcess() { - MessageLoop* loop = mozilla::layers::CompositorBridgeParent::CompositorLoop(); + MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop(); RefPtr vmp = new VRManagerParent(loop, nullptr, base::GetCurrentProcId()); - vmp->mCompositorThreadHolder = layers::GetCompositorThreadHolder(); + vmp->mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton(); vmp->mSelfRef = vmp; loop->PostTask(NewRunnableFunction(RegisterVRManagerInCompositorThread, vmp.get())); return vmp.get(); @@ -112,8 +105,7 @@ void VRManagerParent::ActorDestroy(ActorDestroyReason why) { UnregisterFromManager(); - MessageLoop::current()->PostTask( - NewRunnableMethod(this, &VRManagerParent::DeferredDestroy)); + MessageLoop::current()->PostTask(NewRunnableMethod(this, &VRManagerParent::DeferredDestroy)); } mozilla::ipc::IToplevelProtocol* @@ -140,7 +132,7 @@ VRManagerParent::CloneToplevel(const InfallibleTArray runnable = - NS_NewRunnableMethod(aThisThread, &nsIThread::Shutdown); - NS_DispatchToMainThread(runnable); + NS_DispatchToMainThread(NewRunnableMethod(aThisThread, &nsIThread::Shutdown)); } /** diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index 6afac26380..51cbd493fa 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -437,9 +437,7 @@ RasterImage::OnSurfaceDiscarded() { MOZ_ASSERT(mProgressTracker); - nsCOMPtr runnable = - NS_NewRunnableMethod(mProgressTracker, &ProgressTracker::OnDiscard); - NS_DispatchToMainThread(runnable); + NS_DispatchToMainThread(NewRunnableMethod(mProgressTracker, &ProgressTracker::OnDiscard)); } //****************************************************************************** diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index 3f08dd4310..b8d8950433 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -1063,9 +1063,7 @@ VectorImage::OnSurfaceDiscarded() { MOZ_ASSERT(mProgressTracker); - nsCOMPtr runnable = - NS_NewRunnableMethod(mProgressTracker, &ProgressTracker::OnDiscard); - NS_DispatchToMainThread(runnable); + NS_DispatchToMainThread(NewRunnableMethod(mProgressTracker, &ProgressTracker::OnDiscard)); } //****************************************************************************** diff --git a/image/imgRequestProxy.cpp b/image/imgRequestProxy.cpp index b8fb700db9..ba3e3cf182 100644 --- a/image/imgRequestProxy.cpp +++ b/image/imgRequestProxy.cpp @@ -354,9 +354,7 @@ imgRequestProxy::CancelAndForgetObserver(nsresult aStatus) mIsInLoadGroup = oldIsInLoadGroup; if (mIsInLoadGroup) { - nsCOMPtr ev = - NS_NewRunnableMethod(this, &imgRequestProxy::DoRemoveFromLoadGroup); - NS_DispatchToCurrentThread(ev); + NS_DispatchToCurrentThread(NewRunnableMethod(this, &imgRequestProxy::DoRemoveFromLoadGroup)); } NullOutListener(); diff --git a/ipc/chromium/src/base/task.h b/ipc/chromium/src/base/task.h index b00451cf94..07787d64b1 100644 --- a/ipc/chromium/src/base/task.h +++ b/ipc/chromium/src/base/task.h @@ -303,16 +303,21 @@ class RunnableMethod : public mozilla::CancelableRunnable, Params params_; }; +namespace dont_add_new_uses_of_this { + +// Don't add new uses of this!!!! template -inline already_AddRefed +inline already_AddRefed NewRunnableMethod(T* object, Method method, Args&&... args) { typedef mozilla::Tuple::Type...> ArgsTuple; - RefPtr t = + RefPtr t = new RunnableMethod(object, method, mozilla::MakeTuple(mozilla::Forward(args)...)); return t.forget(); } +} // namespace dont_add_new_uses_of_this + // RunnableFunction and NewRunnableFunction implementation --------------------- template @@ -342,7 +347,7 @@ class RunnableFunction : public mozilla::CancelableRunnable { template inline already_AddRefed -NewRunnableFunction(Function function, Args&&... args) { +NewCancelableRunnableFunction(Function function, Args&&... args) { typedef mozilla::Tuple::Type...> ArgsTuple; RefPtr t = new RunnableFunction(function, @@ -350,4 +355,14 @@ NewRunnableFunction(Function function, Args&&... args) { return t.forget(); } +template +inline already_AddRefed +NewRunnableFunction(Function function, Args&&... args) { + typedef mozilla::Tuple::Type...> ArgsTuple; + RefPtr t = + new RunnableFunction(function, + mozilla::MakeTuple(mozilla::Forward(args)...)); + return t.forget(); +} + #endif // BASE_TASK_H_ diff --git a/ipc/glue/BackgroundImpl.cpp b/ipc/glue/BackgroundImpl.cpp index 3b7235613f..5a77e436dc 100644 --- a/ipc/glue/BackgroundImpl.cpp +++ b/ipc/glue/BackgroundImpl.cpp @@ -433,9 +433,8 @@ private: ChildImpl* actor; threadLocalInfo->mActor.forget(&actor); - nsCOMPtr releaser = - NS_NewNonOwningRunnableMethod(actor, &ChildImpl::Release); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(releaser)); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewNonOwningRunnableMethod(actor, &ChildImpl::Release))); } } delete threadLocalInfo; @@ -1003,11 +1002,8 @@ ParentImpl::GetContentParent(PBackgroundParent* aBackgroundActor) // it for us. This is safe since we are guaranteed that our AddRef runnable // will run before the reference we hand out can be released, and the // ContentParent can't die as long as the existing reference is maintained. - nsCOMPtr runnable = - NS_NewNonOwningRunnableMethod(actor->mContent, &ContentParent::AddRef); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewNonOwningRunnableMethod(actor->mContent, &ContentParent::AddRef))); } return already_AddRefed(actor->mContent.get()); @@ -1271,11 +1267,8 @@ ParentImpl::Destroy() AssertIsInMainProcess(); - nsCOMPtr destroyRunnable = - NS_NewNonOwningRunnableMethod(this, &ParentImpl::MainThreadActorDestroy); - MOZ_ASSERT(destroyRunnable); - - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(destroyRunnable)); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewNonOwningRunnableMethod(this, &ParentImpl::MainThreadActorDestroy))); } void @@ -1363,11 +1356,9 @@ ParentImpl::ActorDestroy(ActorDestroyReason aWhy) // IPDL is about to call MessageChannel::Clear() on this thread! To avoid // racing with the main thread we must ensure that the MessageChannel lives // long enough to be cleared in this call stack. - nsCOMPtr destroyRunnable = - NS_NewNonOwningRunnableMethod(this, &ParentImpl::Destroy); - MOZ_ASSERT(destroyRunnable); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(destroyRunnable)); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToCurrentThread(NewNonOwningRunnableMethod(this, &ParentImpl::Destroy))); } NS_IMPL_ISUPPORTS(ParentImpl::ShutdownObserver, nsIObserver) diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index dce2d30ccd..0a2296c41d 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -81,13 +81,6 @@ ShouldHaveDirectoryService() return GeckoProcessType_Default == XRE_GetProcessType(); } -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(GeckoChildProcessHost* obj) { } - static void ReleaseCallee(GeckoChildProcessHost* obj) { } -}; - /*static*/ base::ChildPrivileges GeckoChildProcessHost::DefaultChildPrivileges() @@ -351,9 +344,10 @@ GeckoChildProcessHost::SyncLaunch(std::vector aExtraOpts, int aTime MessageLoop* ioLoop = XRE_GetIOMessageLoop(); NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI"); - ioLoop->PostTask(NewRunnableMethod(this, - &GeckoChildProcessHost::RunPerformAsyncLaunch, - aExtraOpts, arch)); + ioLoop->PostTask(NewNonOwningRunnableMethod + , base::ProcessArchitecture> + (this, &GeckoChildProcessHost::RunPerformAsyncLaunch, + aExtraOpts, arch)); return WaitUntilConnected(aTimeoutMs); } @@ -365,9 +359,11 @@ GeckoChildProcessHost::AsyncLaunch(std::vector aExtraOpts, PrepareLaunch(); MessageLoop* ioLoop = XRE_GetIOMessageLoop(); - ioLoop->PostTask(NewRunnableMethod(this, - &GeckoChildProcessHost::RunPerformAsyncLaunch, - aExtraOpts, arch)); + + ioLoop->PostTask(NewNonOwningRunnableMethod + , base::ProcessArchitecture> + (this, &GeckoChildProcessHost::RunPerformAsyncLaunch, + aExtraOpts, arch)); // This may look like the sync launch wait, but we only delay as // long as it takes to create the channel. @@ -423,9 +419,10 @@ GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts) PrepareLaunch(); MessageLoop* ioLoop = XRE_GetIOMessageLoop(); - ioLoop->PostTask(NewRunnableMethod(this, - &GeckoChildProcessHost::RunPerformAsyncLaunch, - aExtraOpts, base::GetCurrentProcessArchitecture())); + ioLoop->PostTask(NewNonOwningRunnableMethod + , base::ProcessArchitecture> + (this, &GeckoChildProcessHost::RunPerformAsyncLaunch, + aExtraOpts, base::GetCurrentProcessArchitecture())); MonitorAutoLock lock(mMonitor); while (mProcessState < PROCESS_CREATED) { diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 86a8f1ccdd..79e1c8cd43 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -101,13 +101,6 @@ using mozilla::dom::ScriptSettingsInitialized; using mozilla::MonitorAutoLock; using mozilla::MonitorAutoUnlock; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::ipc::MessageChannel* obj) { } - static void ReleaseCallee(mozilla::ipc::MessageChannel* obj) { } -}; - #define IPC_ASSERT(_cond, ...) \ do { \ if (!(_cond)) \ @@ -502,13 +495,12 @@ MessageChannel::MessageChannel(MessageListener *aListener) mIsSyncWaitingOnNonMainThread = false; #endif - mDequeueOneTask = new RefCountedTask(NewRunnableMethod( - this, - &MessageChannel::OnMaybeDequeueOne)); + RefPtr runnable = + NewNonOwningCancelableRunnableMethod(this, &MessageChannel::OnMaybeDequeueOne); + mDequeueOneTask = new RefCountedTask(runnable.forget()); - mOnChannelConnectedTask = new RefCountedTask(NewRunnableMethod( - this, - &MessageChannel::DispatchOnChannelConnected)); + runnable = NewNonOwningCancelableRunnableMethod(this, &MessageChannel::DispatchOnChannelConnected); + mOnChannelConnectedTask = new RefCountedTask(runnable.forget()); #ifdef OS_WIN mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); @@ -693,8 +685,10 @@ MessageChannel::Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side MonitorAutoLock lock(*mMonitor); mChannelState = ChannelOpening; - aTargetLoop->PostTask( - NewRunnableMethod(aTargetChan, &MessageChannel::OnOpenAsSlave, this, oppSide)); + aTargetLoop->PostTask(NewNonOwningRunnableMethod + (aTargetChan, + &MessageChannel::OnOpenAsSlave, + this, oppSide)); while (ChannelOpening == mChannelState) mMonitor->Wait(); @@ -2100,7 +2094,7 @@ MessageChannel::OnNotifyMaybeChannelError() if (IsOnCxxStack()) { mChannelErrorTask = - NewRunnableMethod(this, &MessageChannel::OnNotifyMaybeChannelError); + NewNonOwningCancelableRunnableMethod(this, &MessageChannel::OnNotifyMaybeChannelError); RefPtr task = mChannelErrorTask; // 10 ms delay is completely arbitrary mWorkerLoop->PostDelayedTask(task.forget(), 10); @@ -2120,7 +2114,7 @@ MessageChannel::PostErrorNotifyTask() // This must be the last code that runs on this thread! mChannelErrorTask = - NewRunnableMethod(this, &MessageChannel::OnNotifyMaybeChannelError); + NewNonOwningCancelableRunnableMethod(this, &MessageChannel::OnNotifyMaybeChannelError); RefPtr task = mChannelErrorTask; mWorkerLoop->PostTask(task.forget()); } diff --git a/ipc/glue/MessageLink.cpp b/ipc/glue/MessageLink.cpp index 2288854615..1dc56687c9 100644 --- a/ipc/glue/MessageLink.cpp +++ b/ipc/glue/MessageLink.cpp @@ -31,13 +31,6 @@ extern "C" char* PrintJSStack(); using namespace mozilla; using namespace std; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::ipc::ProcessLink* obj) { } - static void ReleaseCallee(mozilla::ipc::ProcessLink* obj) { } -}; - // We rely on invariants about the lifetime of the transport: // // - outlives this MessageChannel @@ -50,12 +43,6 @@ struct RunnableMethodTraits // Transport, because whatever task triggers its deletion only runs on // the IO thread, and only runs after this MessageChannel is done with // the Transport. -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::ipc::MessageChannel::Transport* obj) { } - static void ReleaseCallee(mozilla::ipc::MessageChannel::Transport* obj) { } -}; namespace mozilla { namespace ipc { @@ -131,14 +118,12 @@ ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Sid // Transport::Connect() has not been called. Call it so // we start polling our pipe and processing outgoing // messages. - mIOLoop->PostTask( - NewRunnableMethod(this, &ProcessLink::OnChannelOpened)); + mIOLoop->PostTask(NewNonOwningRunnableMethod(this, &ProcessLink::OnChannelOpened)); } else { // Transport::Connect() has already been called. Take // over the channel from the previous listener and process // any queued messages. - mIOLoop->PostTask( - NewRunnableMethod(this, &ProcessLink::OnTakeConnectedChannel)); + mIOLoop->PostTask(NewNonOwningRunnableMethod(this, &ProcessLink::OnTakeConnectedChannel)); } #ifdef MOZ_NUWA_PROCESS @@ -165,8 +150,7 @@ ProcessLink::EchoMessage(Message *msg) mChan->AssertWorkerThread(); mChan->mMonitor->AssertCurrentThreadOwns(); - mIOLoop->PostTask( - NewRunnableMethod(this, &ProcessLink::OnEchoMessage, msg)); + mIOLoop->PostTask(NewNonOwningRunnableMethod(this, &ProcessLink::OnEchoMessage, msg)); // OnEchoMessage takes ownership of |msg| } @@ -212,8 +196,7 @@ ProcessLink::SendMessage(Message *msg) #endif #endif - mIOLoop->PostTask( - NewRunnableMethod(mTransport, &Transport::Send, msg)); + mIOLoop->PostTask(NewNonOwningRunnableMethod(mTransport, &Transport::Send, msg)); } void @@ -222,7 +205,7 @@ ProcessLink::SendClose() mChan->AssertWorkerThread(); mChan->mMonitor->AssertCurrentThreadOwns(); - mIOLoop->PostTask(NewRunnableMethod(this, &ProcessLink::OnCloseChannel)); + mIOLoop->PostTask(NewNonOwningRunnableMethod(this, &ProcessLink::OnCloseChannel)); } ThreadLink::ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan) diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.h b/ipc/ipdl/test/cxx/IPDLUnitTests.h index bb50ecf5bf..544bc0ffb8 100644 --- a/ipc/ipdl/test/cxx/IPDLUnitTests.h +++ b/ipc/ipdl/test/cxx/IPDLUnitTests.h @@ -29,7 +29,7 @@ namespace _ipdltest { //----------------------------------------------------------------------------- // both processes -const char* const IPDLUnitTestName(); +const char* IPDLUnitTestName(); // NB: these are named like the similar functions in // xpcom/test/TestHarness.h. The names should nominally be kept in diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp index ddeceaa1f9..23ced5c302 100644 --- a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp +++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp @@ -47,7 +47,7 @@ DeleteChildActor(); char* gIPDLUnitTestName = nullptr; -const char* const +const char* IPDLUnitTestName() { if (!gIPDLUnitTestName) { @@ -97,7 +97,7 @@ ${STRING_TO_ENUMS} } -const char* const +const char* IPDLUnitTestToString(IPDLUnitTestType aTest) { switch (aTest) { @@ -273,7 +273,7 @@ DeleteSubprocess(MessageLoop* uiLoop) { // pong to QuitXPCOM delete gSubprocess; - uiLoop->PostTask(FROM_HERE, NewRunnableFunction(QuitXPCOM)); + uiLoop->PostTask(NewRunnableFunction(QuitXPCOM)); } void @@ -281,7 +281,6 @@ DeferredParentShutdown() { // ping to DeleteSubprocess XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, NewRunnableFunction(DeleteSubprocess, MessageLoop::current())); } @@ -316,12 +315,12 @@ QuitParent() if (gChildThread) { gParentDone = true; MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableFunction(TryThreadedShutdown)); + NewRunnableFunction(TryThreadedShutdown)); } else { // defer "real" shutdown to avoid *Channel::Close() racing with the // deletion of the subprocess MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableFunction(DeferredParentShutdown)); + NewRunnableFunction(DeferredParentShutdown)); } } @@ -337,10 +336,10 @@ QuitChild() { if (gChildThread) { // Threaded-mode test gParentMessageLoop->PostTask( - FROM_HERE, NewRunnableFunction(ChildCompleted)); + NewRunnableFunction(ChildCompleted)); } else { // Process-mode test MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableFunction(ChildDie)); + NewRunnableFunction(ChildDie)); } } diff --git a/ipc/ipdl/test/cxx/TestActorPunning.cpp b/ipc/ipdl/test/cxx/TestActorPunning.cpp index 194a8f61c3..796a3b5d09 100644 --- a/ipc/ipdl/test/cxx/TestActorPunning.cpp +++ b/ipc/ipdl/test/cxx/TestActorPunning.cpp @@ -1,6 +1,7 @@ #include "TestActorPunning.h" #include "IPDLUnitTests.h" // fail etc. +#include "mozilla/unused.h" namespace mozilla { namespace _ipdltest { @@ -123,7 +124,7 @@ ParamTraits::Read(const Message* aMsg, void** aIter, paramType* aResult) { const char* ptr; int len; - aMsg->ReadData(aIter, &ptr, &len); + mozilla::Unused << aMsg->ReadData(aIter, &ptr, &len); return true; } diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.cpp b/ipc/ipdl/test/cxx/TestBridgeMain.cpp index e68065d483..9856a43cf0 100644 --- a/ipc/ipdl/test/cxx/TestBridgeMain.cpp +++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp @@ -5,13 +5,6 @@ using namespace std; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestBridgeMainSubChild* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestBridgeMainSubChild* obj) { } -}; - namespace mozilla { namespace _ipdltest { @@ -73,11 +66,9 @@ TestBridgeMainSubParent::ActorDestroy(ActorDestroyReason why) // which needs the top-level actor (this) to stay alive a little // longer so other things can be cleaned up. MessageLoop::current()->PostTask( - FROM_HERE, - new DeleteTask(this)); + do_AddRef(new DeleteTask(this))); XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(mTransport)); + do_AddRef(new DeleteTask(mTransport))); } //----------------------------------------------------------------------------- @@ -118,8 +109,7 @@ TestBridgeMainChild::ActorDestroy(ActorDestroyReason why) fail("unexpected destruction!"); // NB: this is kosher because QuitChild() joins with the IO thread XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(mSubprocess)); + do_AddRef(new DeleteTask(mSubprocess))); QuitChild(); } @@ -149,8 +139,7 @@ TestBridgeSubParent::ActorDestroy(ActorDestroyReason why) // which needs the top-level actor (this) to stay alive a little // longer so other things can be cleaned up. MessageLoop::current()->PostTask( - FROM_HERE, - new DeleteTask(this)); + do_AddRef(new DeleteTask(this))); } //----------------------------------------------------------------------------- @@ -207,8 +196,7 @@ TestBridgeMainSubChild::RecvHi() // Need to close the channel without message-processing frames on // the C++ stack MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &TestBridgeMainSubChild::Close)); + NewNonOwningRunnableMethod(this, &TestBridgeMainSubChild::Close)); return true; } @@ -231,11 +219,9 @@ TestBridgeMainSubChild::ActorDestroy(ActorDestroyReason why) // which needs the top-level actor (this) to stay alive a little // longer so other things can be cleaned up. MessageLoop::current()->PostTask( - FROM_HERE, - new DeleteTask(this)); + do_AddRef(new DeleteTask(this))); XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(mTransport)); + do_AddRef(new DeleteTask(mTransport))); } } // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestCancel.cpp b/ipc/ipdl/test/cxx/TestCancel.cpp index 160bdc71c8..8f983b3de8 100644 --- a/ipc/ipdl/test/cxx/TestCancel.cpp +++ b/ipc/ipdl/test/cxx/TestCancel.cpp @@ -2,13 +2,6 @@ #include "IPDLUnitTests.h" // fail etc. -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestCancelParent* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestCancelParent* obj) { } -}; - namespace mozilla { namespace _ipdltest { @@ -84,7 +77,7 @@ bool TestCancelParent::RecvDone() { MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &TestCancelParent::Close)); + NewNonOwningRunnableMethod(this, &TestCancelParent::Close)); return true; } diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.cpp b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp index 308e57395f..5a0fc339b1 100644 --- a/ipc/ipdl/test/cxx/TestCrashCleanup.cpp +++ b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp @@ -44,7 +44,6 @@ void DeleteTheWorld() MutexAutoLock lock(mutex); XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, NewRunnableFunction(DeleteSubprocess, &mutex, &cvar)); cvar.Wait(); @@ -79,14 +78,14 @@ TestCrashCleanupParent::Main() { // NB: has to be enqueued before IO thread's error notification MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableFunction(DeleteTheWorld)); + NewRunnableFunction(DeleteTheWorld)); if (CallDIEDIEDIE()) fail("expected an error!"); Close(); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(Done)); + MessageLoop::current()->PostTask(NewRunnableFunction(Done)); } diff --git a/ipc/ipdl/test/cxx/TestDemon.cpp b/ipc/ipdl/test/cxx/TestDemon.cpp index 38193dc8e5..c349aafbe8 100644 --- a/ipc/ipdl/test/cxx/TestDemon.cpp +++ b/ipc/ipdl/test/cxx/TestDemon.cpp @@ -14,20 +14,6 @@ #include #endif -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestDemonParent* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestDemonParent* obj) { } -}; - -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestDemonChild* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestDemonChild* obj) { } -}; - namespace mozilla { namespace _ipdltest { @@ -190,8 +176,7 @@ TestDemonParent::RunUnlimitedSequence() gFlushStack = false; DoAction(); - MessageLoop::current()->PostTask(FROM_HERE, - NewRunnableMethod(this, &TestDemonParent::RunUnlimitedSequence)); + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(this, &TestDemonParent::RunUnlimitedSequence)); } void @@ -335,8 +320,7 @@ TestDemonChild::RunUnlimitedSequence() gFlushStack = false; DoAction(); - MessageLoop::current()->PostTask(FROM_HERE, - NewRunnableMethod(this, &TestDemonChild::RunUnlimitedSequence)); + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(this, &TestDemonChild::RunUnlimitedSequence)); } void diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp index aa064b0d45..1efa8482e6 100644 --- a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp +++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp @@ -8,13 +8,6 @@ using namespace std; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestEndpointBridgeMainSubChild* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestEndpointBridgeMainSubChild* obj) { } -}; - namespace mozilla { namespace _ipdltest { @@ -78,11 +71,9 @@ TestEndpointBridgeMainSubParent::ActorDestroy(ActorDestroyReason why) // which needs the top-level actor (this) to stay alive a little // longer so other things can be cleaned up. MessageLoop::current()->PostTask( - FROM_HERE, - new DeleteTask(this)); + do_AddRef(new DeleteTask(this))); XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(GetTransport())); + do_AddRef(new DeleteTask(GetTransport()))); } //----------------------------------------------------------------------------- @@ -126,8 +117,7 @@ TestEndpointBridgeMainChild::ActorDestroy(ActorDestroyReason why) } // NB: this is kosher because QuitChild() joins with the IO thread XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(mSubprocess)); + do_AddRef(new DeleteTask(mSubprocess))); QuitChild(); } @@ -174,8 +164,7 @@ TestEndpointBridgeSubParent::ActorDestroy(ActorDestroyReason why) // which needs the top-level actor (this) to stay alive a little // longer so other things can be cleaned up. MessageLoop::current()->PostTask( - FROM_HERE, - new DeleteTask(this)); + do_AddRef(new DeleteTask(this))); } //----------------------------------------------------------------------------- @@ -238,8 +227,7 @@ TestEndpointBridgeMainSubChild::RecvHi() // Need to close the channel without message-processing frames on // the C++ stack MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &TestEndpointBridgeMainSubChild::Close)); + NewNonOwningRunnableMethod(this, &TestEndpointBridgeMainSubChild::Close)); return true; } @@ -263,11 +251,9 @@ TestEndpointBridgeMainSubChild::ActorDestroy(ActorDestroyReason why) // which needs the top-level actor (this) to stay alive a little // longer so other things can be cleaned up. MessageLoop::current()->PostTask( - FROM_HERE, - new DeleteTask(this)); + do_AddRef(new DeleteTask(this))); XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(GetTransport())); + do_AddRef(new DeleteTask(GetTransport()))); } } // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp index 76e87846ba..27966b2676 100644 --- a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp +++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp @@ -7,20 +7,6 @@ #include "IPDLUnitTests.h" // fail etc. -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestEndpointOpensChild* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestEndpointOpensChild* obj) { } -}; - -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest2::TestEndpointOpensOpenedChild* obj) { } - static void ReleaseCallee(mozilla::_ipdltest2::TestEndpointOpensOpenedChild* obj) { } -}; - using namespace mozilla::ipc; using base::ProcessHandle; @@ -85,7 +71,6 @@ TestEndpointOpensParent::RecvStartSubprotocol( TestEndpointOpensOpenedParent* a = new TestEndpointOpensOpenedParent(); gParentThread->message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(OpenParent, a, mozilla::Move(endpoint))); return true; @@ -134,8 +119,7 @@ ShutdownTestEndpointOpensOpenedParent(TestEndpointOpensOpenedParent* parent, // Now delete the transport, which has to happen after the // top-level actor is deleted. XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(transport)); + do_AddRef(new DeleteTask(transport))); } void @@ -151,7 +135,6 @@ TestEndpointOpensOpenedParent::ActorDestroy(ActorDestroyReason why) // which needs the top-level actor (this) to stay alive a little // longer so other things can be cleaned up. gParentThread->message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(ShutdownTestEndpointOpensOpenedParent, this, GetTransport())); } @@ -208,7 +191,6 @@ TestEndpointOpensChild::RecvStart() TestEndpointOpensOpenedChild* a = new TestEndpointOpensOpenedChild(); gChildThread->message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(OpenChild, a, mozilla::Move(child))); if (!SendStartSubprotocol(parent)) { @@ -248,8 +230,7 @@ TestEndpointOpensOpenedChild::RecvHi() // Need to close the channel without message-processing frames on // the C++ stack MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &TestEndpointOpensOpenedChild::Close)); + NewNonOwningRunnableMethod(this, &TestEndpointOpensOpenedChild::Close)); return true; } @@ -271,13 +252,11 @@ ShutdownTestEndpointOpensOpenedChild(TestEndpointOpensOpenedChild* child, // Now delete the transport, which has to happen after the // top-level actor is deleted. XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(transport)); + do_AddRef(new DeleteTask(transport))); // Kick off main-thread shutdown. gMainThread->PostTask( - FROM_HERE, - NewRunnableMethod(gOpensChild, &TestEndpointOpensChild::Close)); + NewNonOwningRunnableMethod(gOpensChild, &TestEndpointOpensChild::Close)); } void @@ -294,7 +273,6 @@ TestEndpointOpensOpenedChild::ActorDestroy(ActorDestroyReason why) // longer so other things can be cleaned up. Defer shutdown to // let cleanup finish. gChildThread->message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(ShutdownTestEndpointOpensOpenedChild, this, GetTransport())); } diff --git a/ipc/ipdl/test/cxx/TestHangs.cpp b/ipc/ipdl/test/cxx/TestHangs.cpp index ef80e02e15..b96823aee3 100644 --- a/ipc/ipdl/test/cxx/TestHangs.cpp +++ b/ipc/ipdl/test/cxx/TestHangs.cpp @@ -6,13 +6,6 @@ using base::KillProcess; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestHangsParent* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestHangsParent* obj) { } -}; - namespace mozilla { namespace _ipdltest { @@ -82,7 +75,7 @@ TestHangsParent::ShouldContinueFromReplyTimeout() // reply should be here; we'll post a task to shut things down. // This must be after OnMaybeDequeueOne() in the event queue. MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &TestHangsParent::CleanUp)); + NewNonOwningRunnableMethod(this, &TestHangsParent::CleanUp)); GetIPCChannel()->CloseWithTimeout(); diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp index 238b040d17..748f1c5e65 100644 --- a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp +++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp @@ -44,7 +44,6 @@ void DeleteTheWorld() MutexAutoLock lock(mutex); XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, NewRunnableFunction(DeleteSubprocess, &mutex, &cvar)); cvar.Wait(); @@ -101,7 +100,7 @@ TestInterruptErrorCleanupParent::Main() // errors/crashes. MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableFunction(DeleteTheWorld)); + NewRunnableFunction(DeleteTheWorld)); // it's a failure if this *succeeds* if (CallError()) @@ -118,7 +117,7 @@ TestInterruptErrorCleanupParent::Main() // notification enqueued by AsyncChannel, because that event is // enqueued within the same mutex that ends up signaling the // wakeup-on-error of |CallError()| above - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(Done)); + MessageLoop::current()->PostTask(NewRunnableFunction(Done)); } void diff --git a/ipc/ipdl/test/cxx/TestInterruptRaces.cpp b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp index a5a566777a..6986506bb8 100644 --- a/ipc/ipdl/test/cxx/TestInterruptRaces.cpp +++ b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp @@ -4,14 +4,6 @@ using mozilla::ipc::MessageChannel; -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestInterruptRacesParent* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestInterruptRacesParent* obj) { } -}; - - namespace mozilla { namespace _ipdltest { @@ -36,8 +28,7 @@ bool TestInterruptRacesParent::RecvStartRace() { MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &TestInterruptRacesParent::OnRaceTime)); + NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::OnRaceTime)); return true; } @@ -53,8 +44,7 @@ TestInterruptRacesParent::OnRaceTime() mHasReply = true; MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &TestInterruptRacesParent::Test2)); + NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::Test2)); } bool @@ -81,8 +71,7 @@ TestInterruptRacesParent::Test2() puts(" passed"); MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &TestInterruptRacesParent::Test3)); + NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::Test3)); } bool diff --git a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp index 7f18c4c687..9f766798ab 100644 --- a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp +++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp @@ -3,14 +3,6 @@ #include "IPDLUnitTests.h" // fail etc. #include "IPDLUnitTestSubprocess.h" -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestInterruptShutdownRaceParent* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestInterruptShutdownRaceParent* obj) { } -}; - - namespace mozilla { namespace _ipdltest { @@ -59,9 +51,8 @@ TestInterruptShutdownRaceParent::RecvStartDeath() // this will be ordered before the OnMaybeDequeueOne event of // Orphan in the queue MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, - &TestInterruptShutdownRaceParent::StartShuttingDown)); + NewNonOwningRunnableMethod(this, + &TestInterruptShutdownRaceParent::StartShuttingDown)); return true; } @@ -82,12 +73,10 @@ TestInterruptShutdownRaceParent::StartShuttingDown() delete static_cast(gParentActor); gParentActor = nullptr; - XRE_GetIOMessageLoop()->PostTask(FROM_HERE, - NewRunnableFunction(DeleteSubprocess)); + XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(DeleteSubprocess)); // this is ordered after the OnMaybeDequeueOne event in the queue - MessageLoop::current()->PostTask(FROM_HERE, - NewRunnableFunction(Done)); + MessageLoop::current()->PostTask(NewRunnableFunction(Done)); // |this| has been deleted, be mindful } diff --git a/ipc/ipdl/test/cxx/TestNestedLoops.cpp b/ipc/ipdl/test/cxx/TestNestedLoops.cpp index 689ab3b71c..0e0ba496de 100644 --- a/ipc/ipdl/test/cxx/TestNestedLoops.cpp +++ b/ipc/ipdl/test/cxx/TestNestedLoops.cpp @@ -6,13 +6,6 @@ #include "IPDLUnitTests.h" // fail etc. -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestNestedLoopsParent* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestNestedLoopsParent* obj) { } -}; - namespace mozilla { namespace _ipdltest { @@ -53,8 +46,7 @@ TestNestedLoopsParent::RecvNonce() // to the inherent race condition in this test, then this event // must be ordered after it in the queue MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &TestNestedLoopsParent::BreakNestedLoop)); + NewNonOwningRunnableMethod(this, &TestNestedLoopsParent::BreakNestedLoop)); // sigh ... spin for a while to let the reply to R arrive puts(" (sleeping to wait for reply to R ... sorry)"); diff --git a/ipc/ipdl/test/cxx/TestOpens.cpp b/ipc/ipdl/test/cxx/TestOpens.cpp index 3895f71fa8..0f6ea07793 100644 --- a/ipc/ipdl/test/cxx/TestOpens.cpp +++ b/ipc/ipdl/test/cxx/TestOpens.cpp @@ -4,20 +4,6 @@ #include "IPDLUnitTests.h" // fail etc. -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestOpensChild* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestOpensChild* obj) { } -}; - -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest2::TestOpensOpenedChild* obj) { } - static void ReleaseCallee(mozilla::_ipdltest2::TestOpensOpenedChild* obj) { } -}; - using namespace mozilla::ipc; using base::ProcessHandle; @@ -78,7 +64,6 @@ TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport, TestOpensOpenedParent* a = new TestOpensOpenedParent(transport); gParentThread->message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(OpenParent, a, transport, otherPid)); return a; @@ -126,8 +111,7 @@ ShutdownTestOpensOpenedParent(TestOpensOpenedParent* parent, // Now delete the transport, which has to happen after the // top-level actor is deleted. XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(transport)); + do_AddRef(new DeleteTask(transport))); } void @@ -142,7 +126,6 @@ TestOpensOpenedParent::ActorDestroy(ActorDestroyReason why) // which needs the top-level actor (this) to stay alive a little // longer so other things can be cleaned up. gParentThread->message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(ShutdownTestOpensOpenedParent, this, mTransport)); } @@ -197,7 +180,6 @@ TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport, TestOpensOpenedChild* a = new TestOpensOpenedChild(transport); gChildThread->message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(OpenChild, a, transport, otherPid)); return a; @@ -229,8 +211,7 @@ TestOpensOpenedChild::RecvHi() // Need to close the channel without message-processing frames on // the C++ stack MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &TestOpensOpenedChild::Close)); + NewNonOwningRunnableMethod(this, &TestOpensOpenedChild::Close)); return true; } @@ -252,13 +233,11 @@ ShutdownTestOpensOpenedChild(TestOpensOpenedChild* child, // Now delete the transport, which has to happen after the // top-level actor is deleted. XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, - new DeleteTask(transport)); + do_AddRef(new DeleteTask(transport))); // Kick off main-thread shutdown. gMainThread->PostTask( - FROM_HERE, - NewRunnableMethod(gOpensChild, &TestOpensChild::Close)); + NewNonOwningRunnableMethod(gOpensChild, &TestOpensChild::Close)); } void @@ -274,7 +253,6 @@ TestOpensOpenedChild::ActorDestroy(ActorDestroyReason why) // longer so other things can be cleaned up. Defer shutdown to // let cleanup finish. gChildThread->message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(ShutdownTestOpensOpenedChild, this, mTransport)); } diff --git a/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp index 2ad3a74958..7402f30d3a 100644 --- a/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp +++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp @@ -6,6 +6,7 @@ using namespace mozilla::ipc; typedef mozilla::ipc::MessageChannel::Message Message; +typedef mozilla::ipc::MessageChannel::MessageInfo MessageInfo; namespace mozilla { namespace _ipdltest { diff --git a/ipc/ipdl/test/cxx/TestRaceDeferral.cpp b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp index 2b88fc87b8..b62d0e2f3c 100644 --- a/ipc/ipdl/test/cxx/TestRaceDeferral.cpp +++ b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp @@ -4,6 +4,7 @@ using namespace mozilla::ipc; typedef mozilla::ipc::MessageChannel::Message Message; +typedef mozilla::ipc::MessageChannel::MessageInfo MessageInfo; namespace mozilla { namespace _ipdltest { diff --git a/ipc/ipdl/test/cxx/TestStackHooks.cpp b/ipc/ipdl/test/cxx/TestStackHooks.cpp index b65833727e..0f6e93e368 100644 --- a/ipc/ipdl/test/cxx/TestStackHooks.cpp +++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp @@ -81,8 +81,7 @@ TestStackHooksChild::RecvStart() // kick off tests from a runnable so that we can start with // MessageChannel code on the C++ stack - MessageLoop::current()->PostTask(FROM_HERE, - NewRunnableFunction(RunTestsFn)); + MessageLoop::current()->PostTask(NewRunnableFunction(RunTestsFn)); return true; } diff --git a/ipc/ipdl/test/cxx/TestSyncHang.cpp b/ipc/ipdl/test/cxx/TestSyncHang.cpp index 2035ac5320..830aa3fb74 100644 --- a/ipc/ipdl/test/cxx/TestSyncHang.cpp +++ b/ipc/ipdl/test/cxx/TestSyncHang.cpp @@ -35,7 +35,6 @@ DeferredSyncHangParentShutdown() { // ping to DeleteSubprocess XRE_GetIOMessageLoop()->PostTask( - FROM_HERE, NewRunnableFunction(DeleteSyncHangSubprocess, MessageLoop::current())); } @@ -49,8 +48,7 @@ TestSyncHangParent::Main() if (launched) fail("Calling SyncLaunch with an invalid path should return false"); - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableFunction(DeferredSyncHangParentShutdown)); + MessageLoop::current()->PostTask(NewRunnableFunction(DeferredSyncHangParentShutdown)); Close(); } diff --git a/ipc/ipdl/test/cxx/TestUrgency.cpp b/ipc/ipdl/test/cxx/TestUrgency.cpp index a18105ff3b..b9b05bceaf 100644 --- a/ipc/ipdl/test/cxx/TestUrgency.cpp +++ b/ipc/ipdl/test/cxx/TestUrgency.cpp @@ -7,13 +7,6 @@ #include #endif -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestUrgencyParent* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestUrgencyParent* obj) { } -}; - namespace mozilla { namespace _ipdltest { diff --git a/ipc/ipdl/test/cxx/TestUrgentHangs.cpp b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp index f2ca7839a2..b798ae18d9 100644 --- a/ipc/ipdl/test/cxx/TestUrgentHangs.cpp +++ b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp @@ -11,13 +11,6 @@ #include #endif -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(mozilla::_ipdltest::TestUrgentHangsParent* obj) { } - static void ReleaseCallee(mozilla::_ipdltest::TestUrgentHangsParent* obj) { } -}; - namespace mozilla { namespace _ipdltest { @@ -56,8 +49,7 @@ TestUrgentHangsParent::Main() // Do a second round of testing once the reply to Test2 comes back. MessageLoop::current()->PostDelayedTask( - FROM_HERE, - NewRunnableMethod(this, &TestUrgentHangsParent::SecondStage), + NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::SecondStage), 3000); } @@ -75,8 +67,7 @@ TestUrgentHangsParent::SecondStage() fail("sending Test4_1"); MessageLoop::current()->PostDelayedTask( - FROM_HERE, - NewRunnableMethod(this, &TestUrgentHangsParent::ThirdStage), + NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::ThirdStage), 3000); } @@ -99,8 +90,7 @@ TestUrgentHangsParent::ThirdStage() // Close the channel after the child finishes its work in RecvTest5. MessageLoop::current()->PostDelayedTask( - FROM_HERE, - NewRunnableMethod(this, &TestUrgentHangsParent::Close), + NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::Close), 3000); } diff --git a/js/public/Proxy.h b/js/public/Proxy.h index 9470b70e8d..42c3b9e407 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -272,7 +272,7 @@ class JS_FRIEND_API(BaseProxyHandler) /* Non-standard but conceptual kin to {g,s}etPrototype, so these live here. */ virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, - MutableHandleObject protop) const; + MutableHandleObject protop) const = 0; virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const; virtual bool preventExtensions(JSContext* cx, HandleObject proxy, diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 522340f323..71aa3ea86d 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -232,7 +232,7 @@ MapIteratorObject::createResultPair(JSContext* cx) if (!resultPairObj) return nullptr; - Rooted proto(cx, resultPairObj->getTaggedProto()); + Rooted proto(cx, resultPairObj->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultPairObj->getClass(), proto); if (!group) return nullptr; diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index b59913789a..c1a47c0dc6 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -612,7 +612,7 @@ js::ObjectCreateImpl(JSContext* cx, HandleObject proto, NewObjectKind newKind, PlainObject* js::ObjectCreateWithTemplate(JSContext* cx, HandlePlainObject templateObj) { - RootedObject proto(cx, templateObj->getProto()); + RootedObject proto(cx, templateObj->staticPrototype()); RootedObjectGroup group(cx, templateObj->group()); return ObjectCreateImpl(cx, proto, GenericObject, group); } diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 5ec36bed24..effda51c0d 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -1609,12 +1609,12 @@ js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* rx, JSObject* proto, u return true; } - if (rx->hasLazyPrototype()) { + if (!rx->hasStaticPrototype()) { *result = false; return true; } - if (rx->getTaggedProto().toObjectOrNull() != proto) { + if (rx->staticPrototype() != proto) { *result = false; return true; } diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 95539441de..a5db65d521 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1679,7 +1679,7 @@ TypedObject::obj_lookupProperty(JSContext* cx, HandleObject obj, HandleId id, return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); @@ -1751,7 +1751,7 @@ TypedObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; @@ -1808,7 +1808,7 @@ TypedObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiv } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; @@ -1836,7 +1836,7 @@ TypedObject::obj_getElement(JSContext* cx, HandleObject obj, HandleValue receive return obj_getArrayElement(cx, typedObj, descr, index, vp); } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; @@ -2019,7 +2019,7 @@ TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, Ob if (IsOwnId(cx, obj, id)) return ReportPropertyError(cx, JSMSG_CANT_DELETE, id); - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) return result.succeed(); diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 1e9dbee7b8..49ef86436c 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -530,7 +530,8 @@ class TypedObject : public JSObject public: TypedProto& typedProto() const { - return getProto()->as(); + // Typed objects' prototypes can't be modified. + return staticPrototype()->as(); } TypeDescr& typeDescr() const { diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 95b81d5ad6..4d7e0621ec 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -785,12 +785,14 @@ IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape, size_t chainDepth = 0; // Walk up the object prototype chain and ensure that all prototypes are // native, and that all prototypes have no setter defined on the property. - for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) { + for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) { chainDepth++; // if prototype is non-native, don't optimize if (!proto->isNative()) return false; + MOZ_ASSERT(proto->hasStaticPrototype()); + // if prototype defines this property in a non-plain way, don't optimize Shape* protoShape = proto->as().lookup(cx, id); if (protoShape && !protoShape->hasDefaultSetter()) @@ -2360,13 +2362,13 @@ SetElemAddHasSameShapes(ICSetElem_DenseOrUnboxedArrayAdd* stub, JSObject* obj) if (obj->maybeShape() != nstub->shape(0)) return false; - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); for (size_t i = 0; i < stub->protoChainDepth(); i++) { if (!proto->isNative()) return false; if (proto->as().lastProperty() != nstub->shape(i + 1)) return false; - proto = obj->getProto(); + proto = obj->staticPrototype(); if (!proto) { if (i != stub->protoChainDepth() - 1) return false; @@ -2487,12 +2489,12 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, // Scan the prototype and shape chain to make sure that this is not the case. if (obj->isIndexed()) return false; - JSObject* curObj = obj->getProto(); + JSObject* curObj = obj->staticPrototype(); while (curObj) { ++*protoDepthOut; if (!curObj->isNative() || curObj->isIndexed()) return false; - curObj = curObj->getProto(); + curObj = curObj->staticPrototype(); } if (*protoDepthOut > ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH) @@ -3706,7 +3708,7 @@ TryAttachGlobalNameValueStub(JSContext* cx, HandleScript script, jsbytecode* pc, if (current == globalLexical) { current = &globalLexical->global(); } else { - JSObject* proto = current->getProto(); + JSObject* proto = current->staticPrototype(); if (!proto || !proto->is()) return true; current = &proto->as(); @@ -3781,7 +3783,7 @@ TryAttachGlobalNameAccessorStub(JSContext* cx, HandleScript script, jsbytecode* shape = current->lookup(cx, id); if (shape) break; - JSObject* proto = current->getProto(); + JSObject* proto = current->staticPrototype(); if (!proto || !proto->is()) return true; current = &proto->as(); @@ -7770,12 +7772,14 @@ TryAttachInstanceOfStub(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallba // clobber it. if (!js::FunctionHasDefaultHasInstance(fun, cx->wellKnownSymbols())) return true; + // Refuse to optimize any function whose [[Prototype]] isn't // Function.prototype. - if (fun->hasLazyPrototype() || fun->hasUncacheableProto()) + if (!fun->hasStaticPrototype() || fun->hasUncacheableProto()) return true; + Value funProto = cx->global()->getPrototype(JSProto_Function); - if (funProto.isObject() && fun->getProto() != &funProto.toObject()) + if (funProto.isObject() && fun->staticPrototype() != &funProto.toObject()) return true; Shape* shape = fun->lookupPure(cx->names().prototype); diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index c376dd5456..075f4b0eae 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -137,12 +137,10 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, if (obj->hasUncacheableProto()) { // If the shape does not imply the proto, emit an explicit proto guard. - writer.guardProto(objId, obj->getProto()); + writer.guardProto(objId, obj->staticPrototype()); } - JSObject* pobj = IsCacheableDOMProxy(obj) - ? obj->getTaggedProto().toObjectOrNull() - : obj->getProto(); + JSObject* pobj = obj->staticPrototype(); if (!pobj) return; @@ -151,12 +149,12 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId protoId = writer.loadObject(pobj); if (pobj->isSingleton()) { // Singletons can have their group's |proto| mutated directly. - writer.guardProto(protoId, pobj->getProto()); + writer.guardProto(protoId, pobj->staticPrototype()); } else { writer.guardGroup(protoId, pobj->group()); } } - pobj = pobj->getProto(); + pobj = pobj->staticPrototype(); } } @@ -199,12 +197,12 @@ EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, // The property does not exist. Guard on everything in the prototype // chain. This is guaranteed to see only Native objects because of // CanAttachNativeGetProp(). - JSObject* proto = obj->getTaggedProto().toObjectOrNull(); + JSObject* proto = obj->taggedProto().toObjectOrNull(); ObjOperandId lastObjId = objId; while (proto) { ObjOperandId protoId = writer.loadProto(lastObjId); writer.guardShape(protoId, proto->as().lastProperty()); - proto = proto->getProto(); + proto = proto->staticPrototype(); lastObjId = protoId; } } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index c514cc6546..08c2abb906 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6281,7 +6281,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction* target, MDefinition* callee) return nullptr; if (!templateObject->is() && !templateObject->is()) return nullptr; - if (templateObject->getProto() != proto) + if (templateObject->staticPrototype() != proto) return nullptr; TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group()); @@ -6328,7 +6328,7 @@ IonBuilder::createThisScriptedBaseline(MDefinition* callee) return nullptr; JSObject* proto = checkNurseryObject(&protov.toObject()); - if (proto != templateObject->getProto()) + if (proto != templateObject->staticPrototype()) return nullptr; TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group()); @@ -8116,7 +8116,7 @@ IonBuilder::testSingletonProperty(JSObject* obj, jsid id) if (ObjectHasExtraOwnProperty(compartment, objKey, id)) return nullptr; - obj = checkNurseryObject(obj->getProto()); + obj = checkNurseryObject(obj->staticPrototype()); } return nullptr; @@ -13826,23 +13826,26 @@ IonBuilder::jsop_instanceof() if (!rhsObject || !rhsObject->is() || rhsObject->isBoundFunction()) break; - // Refuse to optimize anything whose [[Prototype]] isn't Function.prototype + // Refuse to optimize anything whose [[Prototype]] isn't Function.prototype // since we can't guarantee that it uses the default @@hasInstance method. - if (rhsObject->hasUncacheableProto() || rhsObject->hasLazyPrototype()) + if (rhsObject->hasUncacheableProto() || !rhsObject->hasStaticPrototype()) break; + Value funProto = script()->global().getPrototype(JSProto_Function); - if (!funProto.isObject() || rhsObject->getProto() != &funProto.toObject()) + if (!funProto.isObject() || rhsObject->staticPrototype() != &funProto.toObject()) break; + // If the user has supplied their own @@hasInstance method we shouldn't // clobber it. JSFunction* fun = &rhsObject->as(); const WellKnownSymbols* symbols = &compartment->runtime()->wellKnownSymbols(); if (!js::FunctionHasDefaultHasInstance(fun, *symbols)) break; + // Ensure that we will bail if the @@hasInstance property or [[Prototype]] // change. TypeSet::ObjectKey* rhsKey = TypeSet::ObjectKey::get(rhsObject); - if (!rhsKey->hasStableClassAndProto(constraints())) + if (!rhsKey->hasStableClassAndProto(constraints())) break; if (rhsKey->unknownProperties()) diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 44f2a40027..4d07234733 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -422,12 +422,10 @@ GeneratePrototypeGuards(JSContext* cx, IonScript* ion, MacroAssembler& masm, JSO // use objectReg in the rest of this function. masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg); Address proto(scratchReg, ObjectGroup::offsetOfProto()); - masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), failures); + masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->staticPrototype()), failures); } - JSObject* pobj = IsCacheableDOMProxy(obj) - ? obj->getTaggedProto().toObjectOrNull() - : obj->getProto(); + JSObject* pobj = obj->staticPrototype(); if (!pobj) return; while (pobj != holder) { @@ -438,12 +436,14 @@ GeneratePrototypeGuards(JSContext* cx, IonScript* ion, MacroAssembler& masm, JSO // Singletons can have their group's |proto| mutated directly. masm.loadPtr(groupAddr, scratchReg); Address protoAddr(scratchReg, ObjectGroup::offsetOfProto()); - masm.branchPtr(Assembler::NotEqual, protoAddr, ImmGCPtr(pobj->getProto()), failures); + masm.branchPtr(Assembler::NotEqual, protoAddr, ImmGCPtr(pobj->staticPrototype()), + failures); } else { masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), failures); } } - pobj = pobj->getProto(); + + pobj = pobj->staticPrototype(); } } @@ -459,7 +459,7 @@ jit::IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder) * chain and must check for null proto. The prototype chain can be * altered during the lookupProperty call. */ - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (!proto || !proto->isNative()) return false; obj = proto; @@ -500,7 +500,7 @@ IsCacheableNoProperty(JSObject* obj, JSObject* holder, Shape* shape, jsbytecode* while (obj2) { if (!obj2->isNative()) return false; - obj2 = obj2->getProto(); + obj2 = obj2->staticPrototype(); } // The pc is nullptr if the cache is idempotent. We cannot share missing @@ -819,19 +819,20 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, } else { // The property does not exist. Guard on everything in the // prototype chain. - JSObject* proto = obj->getTaggedProto().toObjectOrNull(); + JSObject* proto = obj->staticPrototype(); Register lastReg = object; MOZ_ASSERT(scratchReg != object); while (proto) { masm.loadObjProto(lastReg, scratchReg); // Guard the shape of the current prototype. + MOZ_ASSERT(proto->hasStaticPrototype()); masm.branchPtr(Assembler::NotEqual, Address(scratchReg, JSObject::offsetOfShape()), ImmGCPtr(proto->as().lastProperty()), &prototypeFailures); - proto = proto->getProto(); + proto = proto->staticPrototype(); lastReg = scratchReg; } @@ -1803,7 +1804,7 @@ GetPropertyIC::tryAttachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScri MOZ_ASSERT(monitoredResult()); MOZ_ASSERT(output().hasValue()); - RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull()); + RootedObject checkObj(cx, obj->staticPrototype()); RootedNativeObject holder(cx); RootedShape shape(cx); @@ -2920,7 +2921,7 @@ IsCacheableDOMProxyUnshadowedSetterCall(JSContext* cx, HandleObject obj, HandleI { MOZ_ASSERT(IsCacheableDOMProxy(obj)); - RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull()); + RootedObject checkObj(cx, obj->staticPrototype()); if (!checkObj) return false; @@ -3043,7 +3044,7 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures); // Guard shapes along prototype chain. - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); Register protoReg = tempReg; bool first = true; while (proto) { @@ -3056,7 +3057,7 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att // Ensure that its shape matches. masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, failures); - proto = proto->getProto(); + proto = proto->staticPrototype(); } // Call a stub to (re)allocate dynamic slots, if necessary. @@ -3262,7 +3263,7 @@ PrototypeChainShadowsPropertyAdd(JSContext* cx, JSObject* obj, jsid id) // Walk up the object prototype chain and ensure that all prototypes // are native, and that all prototypes have no getter or setter // defined on the property - for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) { + for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) { // If prototype is non-native, don't optimize if (!proto->isNative()) return true; @@ -3863,7 +3864,7 @@ GetPropertyIC::canAttachDenseElementHole(JSObject* obj, HandleValue idval, Typed if (ClassCanHaveExtraProperties(obj->getClass())) return false; - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (!proto) break; @@ -3899,10 +3900,10 @@ GenerateDenseElementHole(JSContext* cx, MacroAssembler& masm, IonCache::StubAtta if (obj->hasUncacheableProto()) { masm.loadPtr(Address(object, JSObject::offsetOfGroup()), scratchReg); Address proto(scratchReg, ObjectGroup::offsetOfProto()); - masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), &failures); + masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->staticPrototype()), &failures); } - JSObject* pobj = obj->getProto(); + JSObject* pobj = obj->staticPrototype(); while (pobj) { MOZ_ASSERT(pobj->as().lastProperty()); @@ -3928,7 +3929,7 @@ GenerateDenseElementHole(JSContext* cx, MacroAssembler& masm, IonCache::StubAtta Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); masm.branch32(Assembler::NotEqual, initLength, Imm32(0), &failures); - pobj = pobj->getProto(); + pobj = pobj->staticPrototype(); } // Ensure the index is an int32 value. @@ -4332,7 +4333,7 @@ IsDenseElementSetInlineable(JSObject* obj, const Value& idval, ConstantOrRegiste // Scan the prototype and shape chain to make sure that this is not the case. JSObject* curObj = obj; while (curObj) { - // Ensure object is native. + // Ensure object is native. (This guarantees static prototype below.) if (!curObj->isNative()) return false; @@ -4340,7 +4341,7 @@ IsDenseElementSetInlineable(JSObject* obj, const Value& idval, ConstantOrRegiste if (curObj->isIndexed()) return false; - curObj = curObj->getProto(); + curObj = curObj->staticPrototype(); } *checkTypeset = false; diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index cc14918de5..61ca20fb1d 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -2057,7 +2057,7 @@ IonBuilder::inlineObjectCreate(CallInfo& callInfo) // Ensure the argument matches the template object's prototype. MDefinition* arg = callInfo.getArg(0); - if (JSObject* proto = templateObject->getProto()) { + if (JSObject* proto = templateObject->staticPrototype()) { if (IsInsideNursery(proto)) return InliningStatus_NotInlined; diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index f5be1cc1c6..ad222d3cd0 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -5633,7 +5633,7 @@ jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx, if (key->isSingleton()) obj = key->singleton(); else - obj = key->proto().isLazy() ? nullptr : key->proto().toObjectOrNull(); + obj = key->proto().isDynamic() ? nullptr : key->proto().toObjectOrNull(); while (obj) { if (!obj->getClass()->isNative()) @@ -5657,7 +5657,7 @@ jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx, } } - obj = obj->getProto(); + obj = obj->staticPrototype(); } } @@ -5845,7 +5845,7 @@ PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj) HeapTypeSetKey index = key->property(JSID_VOID); if (index.nonData(builder->constraints()) || index.isOwnProperty(builder->constraints())) return true; - obj = obj->getProto(); + obj = obj->staticPrototype(); } while (obj); return false; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 6df746b7c5..004881102d 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -2172,7 +2172,7 @@ JSObject* GetDOMProxyProto(JSObject* obj) { MOZ_ASSERT(IsCacheableDOMProxy(obj)); - return obj->getTaggedProto().toObjectOrNull(); + return obj->staticPrototype(); } // Look up a property's shape on an object, being careful never to do any effectful @@ -2243,30 +2243,25 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy) } } - // Don't handle objects which require a prototype guard. This should - // be uncommon so handling it is likely not worth the complexity. - if (obj->hasUncacheableProto()) - return false; - JSObject* cur = obj; while (cur != holder) { // We cannot assume that we find the holder object on the prototype // chain and must check for null proto. The prototype chain can be // altered during the lookupProperty call. - JSObject* proto; - if (isDOMProxy && cur == obj) - proto = cur->getTaggedProto().toObjectOrNull(); - else - proto = cur->getProto(); + MOZ_ASSERT(!cur->hasDynamicPrototype()); - if (!proto || !proto->isNative()) + // Don't handle objects which require a prototype guard. This should + // be uncommon so handling it is likely not worth the complexity. + if (cur->hasUncacheableProto()) return false; - if (proto->hasUncacheableProto()) + JSObject* proto = cur->staticPrototype(); + if (!proto || !proto->isNative()) return false; cur = proto; } + return true; } @@ -2638,7 +2633,7 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name, return false; } - JSObject* proto = curObj->getTaggedProto().toObjectOrNull(); + JSObject* proto = curObj->staticPrototype(); if (!proto) break; @@ -3057,13 +3052,15 @@ ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm) bool GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle shapes) { - JSObject* curProto = obj->getProto(); + JSObject* curProto = obj->staticPrototype(); for (size_t i = 0; i < protoChainDepth; i++) { if (!shapes.append(curProto->as().lastProperty())) return false; - curProto = curProto->getProto(); + curProto = curProto->staticPrototype(); } - MOZ_ASSERT(!curProto); + + MOZ_ASSERT(!curProto, + "longer prototype chain encountered than this stub permits!"); return true; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 623d978e3d..38859652cf 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1133,7 +1133,7 @@ JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj // The global object's resolve hook is special: JS_ResolveStandardClass // initializes the prototype chain lazily. Only attempt to optimize here // if we know the prototype chain has been initialized. - if (!maybeObj || !maybeObj->getProto()) + if (!maybeObj || !maybeObj->staticPrototype()) return true; if (!JSID_IS_ATOM(id)) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 961121ddcd..2cfe230f96 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4904,9 +4904,9 @@ GetSymbolDescription(HandleSymbol symbol); macro(replace) \ macro(search) \ macro(species) \ + macro(hasInstance) \ macro(split) \ macro(toPrimitive) \ - macro(hasInstance) \ macro(unscopables) enum class SymbolCode : uint32_t { diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index b7124d016b..5c101e5527 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -828,26 +828,33 @@ ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj) obj->getClass(), INT_TO_JSID(0), obj); } +/* + * Whether obj may have indexed properties anywhere besides its dense + * elements. This includes other indexed properties in its shape hierarchy, and + * indexed properties or elements along its prototype chain. + */ bool js::ObjectMayHaveExtraIndexedProperties(JSObject* obj) { - /* - * Whether obj may have indexed properties anywhere besides its dense - * elements. This includes other indexed properties in its shape hierarchy, - * and indexed properties or elements along its prototype chain. - */ + MOZ_ASSERT_IF(obj->hasDynamicPrototype(), !obj->isNative()); if (ObjectMayHaveExtraIndexedOwnProperties(obj)) return true; - while ((obj = obj->getProto()) != nullptr) { + do { + MOZ_ASSERT(obj->hasStaticPrototype(), + "dynamic-prototype objects must be non-native, ergo must " + "have failed ObjectMayHaveExtraIndexedOwnProperties"); + + obj = obj->staticPrototype(); + if (!obj) + return false; // no extra indexed properties found + if (ObjectMayHaveExtraIndexedOwnProperties(obj)) return true; if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0) return true; - } - - return false; + } while (true); } static bool @@ -2700,7 +2707,7 @@ GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint32_t begin, uin do { if (!pobj->isNative() || pobj->getClass()->getResolve() || pobj->getOpsLookupProperty()) return true; - } while ((pobj = pobj->getProto())); + } while ((pobj = pobj->staticPrototype())); // Collect indexed property names. pobj = obj; @@ -2745,7 +2752,7 @@ GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint32_t begin, uin return false; } } - } while ((pobj = pobj->getProto())); + } while ((pobj = pobj->staticPrototype())); // Sort the indexes. Vector tmp(cx); @@ -3609,7 +3616,7 @@ NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, if (!obj->is() && !obj->is()) return NewArray(cx, length, nullptr, newKind); - if (obj->getProto() != cx->global()->maybeGetArrayPrototype()) + if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype()) return NewArray(cx, length, nullptr, newKind); RootedObjectGroup group(cx, obj->getGroup(cx)); diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index ab163983cf..3a4574f26e 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -453,7 +453,7 @@ JSCompartment::wrap(JSContext* cx, MutableHandleObject obj, HandleObject existin RootedObject existing(cx, existingArg); if (existing) { // Is it possible to reuse |existing|? - if (!existing->getTaggedProto().isLazy() || + if (existing->hasStaticPrototype() || // Note: Class asserted above, so all that's left to check is callability existing->isCallable() || obj->isCallable()) diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 493161e37a..4af361c57c 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -353,8 +353,7 @@ JS_FRIEND_API(JSObject*) js::GetPrototypeNoProxy(JSObject* obj) { MOZ_ASSERT(!obj->is()); - MOZ_ASSERT(!obj->getTaggedProto().isLazy()); - return obj->getTaggedProto().toObjectOrNull(); + return obj->staticPrototype(); } JS_FRIEND_API(void) @@ -501,6 +500,13 @@ js::GetObjectProto(JSContext* cx, JS::Handle obj, JS::MutableHandlehasStaticPrototype()); + return obj->staticPrototype(); +} + JS_FRIEND_API(bool) js::GetOriginalEval(JSContext* cx, HandleObject scope, MutableHandleObject eval) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 53a616ceb3..501446706e 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -681,6 +681,9 @@ FunctionHasNativeReserved(JSObject* fun); JS_FRIEND_API(bool) GetObjectProto(JSContext* cx, JS::HandleObject obj, JS::MutableHandleObject proto); +extern JS_FRIEND_API(JSObject*) +GetStaticPrototype(JSObject* obj); + JS_FRIEND_API(bool) GetOriginalEval(JSContext* cx, JS::HandleObject scope, JS::MutableHandleObject eval); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 862f6af626..14157ccf47 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -693,7 +693,6 @@ js::fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp) /* * ES6 (4-25-16) 7.3.19 OrdinaryHasInstance */ - bool js::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, MutableHandleValue v, bool* bp) { @@ -1164,20 +1163,6 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) return out.finishString(); } -bool -js::FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols) -{ - jsid id = SYMBOL_TO_JSID(symbols.hasInstance); - Shape* shape = fun->lookupPure(id); - if (shape) { - if (!shape->hasSlot() || !shape->hasDefaultGetter()) - return false; - const Value hasInstance = fun->as().getSlot(shape->slot()); - return IsNativeFunction(hasInstance, js::fun_symbolHasInstance); - } - return true; -} - JSString* fun_toStringHelper(JSContext* cx, HandleObject obj, unsigned indent) { @@ -1196,6 +1181,20 @@ fun_toStringHelper(JSContext* cx, HandleObject obj, unsigned indent) return FunctionToString(cx, fun, indent != JS_DONT_PRETTY_PRINT); } +bool +js::FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols) +{ + jsid id = SYMBOL_TO_JSID(symbols.hasInstance); + Shape* shape = fun->lookupPure(id); + if (shape) { + if (!shape->hasSlot() || !shape->hasDefaultGetter()) + return false; + const Value hasInstance = fun->as().getSlot(shape->slot()); + return IsNativeFunction(hasInstance, js::fun_symbolHasInstance); + } + return true; +} + bool js::fun_toString(JSContext* cx, unsigned argc, Value* vp) { @@ -2107,7 +2106,7 @@ js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject par * Clone the function, reusing its script. We can use the same group as * the original function provided that its prototype is correct. */ - if (fun->getProto() == clone->getProto()) + if (fun->staticPrototype() == clone->staticPrototype()) clone->setGroup(fun->group()); return clone; } @@ -2260,8 +2259,8 @@ js::ReportIncompatibleMethod(JSContext* cx, CallReceiver call, const Class* clas if (thisv.isObject()) { MOZ_ASSERT(thisv.toObject().getClass() != clasp || !thisv.toObject().isNative() || - !thisv.toObject().getProto() || - thisv.toObject().getProto()->getClass() != clasp); + !thisv.toObject().staticPrototype() || + thisv.toObject().staticPrototype()->getClass() != clasp); } else if (thisv.isString()) { MOZ_ASSERT(clasp != &StringObject::class_); } else if (thisv.isNumber()) { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 3bb8aada2f..962b06b74b 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -111,8 +111,10 @@ Enumerate(JSContext* cx, HandleObject pobj, jsid id, // It's not necessary to add properties to the hash table at the end of // the prototype chain, but custom enumeration behaviors might return // duplicated properties, so always add in such cases. - if ((pobj->is() || pobj->getProto() || pobj->getOpsEnumerate()) && !ht->add(p, id)) - return false; + if (pobj->is() || pobj->staticPrototype() || pobj->getOpsEnumerate()) { + if (!ht->add(p, id)) + return false; + } } // Symbol-keyed properties and nonenumerable properties are skipped unless @@ -701,7 +703,11 @@ VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVecto size_t ind = 0; do { ni->guard_array[ind++].init(ReceiverGuard(pobj)); - pobj = pobj->getProto(); + + // The one caller of this method that passes |numGuards > 0|, does + // so only if the entire chain consists of cacheable objects (that + // necessarily have static prototypes). + pobj = pobj->staticPrototype(); } while (pobj); MOZ_ASSERT(ind == numGuards); } @@ -843,10 +849,10 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleOb CanCompareIterableObjectToCache(obj) && ReceiverGuard(obj) == lastni->guard_array[0]) { - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (CanCompareIterableObjectToCache(proto) && ReceiverGuard(proto) == lastni->guard_array[1] && - !proto->getProto()) + !proto->staticPrototype()) { objp.set(last); UpdateNativeIterator(lastni, obj); @@ -856,12 +862,9 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleOb } } - /* - * The iterator object for JSITER_ENUMERATE never escapes, so we - * don't care for the proper parent/proto to be set. This also - * allows us to re-use a previous iterator object that is not - * currently active. - */ + // The iterator object for JSITER_ENUMERATE never escapes, so we don't + // care that the "proper" prototype is set. This also lets us reuse an + // old, inactive iterator object. { JSObject* pobj = obj; do { @@ -869,11 +872,13 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleOb guards.clear(); goto miss; } + ReceiverGuard guard(pobj); key = (key + (key << 16)) ^ guard.hash(); if (!guards.append(guard)) return false; - pobj = pobj->getProto(); + + pobj = pobj->staticPrototype(); } while (pobj); } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 0c2d7940ec..9c4c82e2cb 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -501,7 +501,7 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level) // generic path below then any non-empty object will be converted to // dictionary mode. RootedShape last(cx, EmptyShape::getInitialShape(cx, nobj->getClass(), - nobj->getTaggedProto(), + nobj->taggedProto(), nobj->numFixedSlots(), nobj->lastProperty()->getObjectFlags())); if (!last) @@ -689,7 +689,7 @@ NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto gc::AllocKind kind, NativeObject* obj) { MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->is()); - MOZ_ASSERT(obj->getTaggedProto() == proto); + MOZ_ASSERT(obj->taggedProto() == proto); return fill(entry, clasp, proto.raw(), kind, obj); } @@ -914,7 +914,7 @@ CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group, return nullptr; if (newKind == SingletonObject) { - Rooted proto(cx, TaggedProto(templateObject->getProto())); + Rooted proto(cx, TaggedProto(templateObject->staticPrototype())); if (!res->splicePrototype(cx, &PlainObject::class_, proto)) return nullptr; } else { @@ -1341,13 +1341,13 @@ InitializePropertiesFromCompatibleNativeObject(JSContext* cx, MOZ_ASSERT(!src->hasPrivate()); RootedShape shape(cx); - if (src->getProto() == dst->getProto()) { + if (src->staticPrototype() == dst->staticPrototype()) { shape = src->lastProperty(); } else { // We need to generate a new shape for dst that has dst's proto but all // the property information from src. Note that we asserted above that // dst's object flags are 0. - shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->getTaggedProto(), + shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->taggedProto(), dst->numFixedSlots(), 0); if (!shape) return false; @@ -1943,7 +1943,7 @@ js::SetClassAndProto(JSContext* cx, HandleObject obj, MOZ_ASSERT(obj == oldproto); break; } - oldproto = oldproto->getProto(); + oldproto = oldproto->staticPrototype(); } if (proto.isObject() && !proto.toObject()->setDelegate(cx)) @@ -1993,7 +1993,7 @@ JSObject::changeToSingleton(JSContext* cx, HandleObject obj) MarkObjectGroupUnknownProperties(cx, obj->group()); ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->getClass(), - obj->getTaggedProto()); + obj->taggedProto()); if (!group) return false; @@ -2295,7 +2295,7 @@ js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** return true; } - obj = obj->getProto(); + obj = obj->staticPrototype(); } while (obj); *objp = nullptr; @@ -2498,13 +2498,11 @@ bool js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary, MutableHandleObject protop) { - if (obj->getTaggedProto().isLazy()) { - MOZ_ASSERT(obj->is()); + if (obj->is()) return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop); - } *isOrdinary = true; - protop.set(obj->getTaggedProto().toObjectOrNull()); + protop.set(obj->staticPrototype()); return true; } @@ -2527,19 +2525,15 @@ JS_ImmutablePrototypesEnabled() bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::ObjectOpResult& result) { - /* - * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's - * {get,set}Prototype and setImmutablePrototype methods mediate access to - * |obj.[[Prototype]]|. The Proxy subsystem is responsible for responding - * to such attempts. - */ - if (obj->hasLazyPrototype()) { + // The proxy trap subsystem fully handles prototype-setting for proxies + // with dynamic [[Prototype]]s. + if (obj->hasDynamicPrototype()) { MOZ_ASSERT(obj->is()); return Proxy::setPrototype(cx, obj, proto, result); } /* Disallow mutation of immutable [[Prototype]]s. */ - if (obj->nonLazyPrototypeIsImmutable() && ImmutablePrototypesEnabled) + if (obj->staticPrototypeIsImmutable() && ImmutablePrototypesEnabled) return result.fail(JSMSG_CANT_SET_PROTO); /* @@ -2576,7 +2570,7 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true. * Since the values in question are objects, we can just compare pointers. */ - if (proto == obj->getProto()) + if (proto == obj->staticPrototype()) return result.succeed(); /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ @@ -2786,7 +2780,7 @@ js::DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, Handle bool js::SetImmutablePrototype(ExclusiveContext* cx, HandleObject obj, bool* succeeded) { - if (obj->hasLazyPrototype()) { + if (obj->hasDynamicPrototype()) { if (!cx->shouldBeJSContext()) return false; return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded); @@ -3471,7 +3465,8 @@ JSObject::dump() if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto"); if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access"); if (obj->wasNewScriptCleared()) fprintf(stderr, " new_script_cleared"); - if (!obj->hasLazyPrototype() && obj->nonLazyPrototypeIsImmutable()) fprintf(stderr, " immutable_prototype"); + if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable()) + fprintf(stderr, " immutable_prototype"); if (obj->isNative()) { NativeObject* nobj = &obj->as(); @@ -3497,9 +3492,9 @@ JSObject::dump() } fprintf(stderr, "proto "); - TaggedProto proto = obj->getTaggedProto(); - if (proto.isLazy()) - fprintf(stderr, ""); + TaggedProto proto = obj->taggedProto(); + if (proto.isDynamic()) + fprintf(stderr, ""); else dumpValue(ObjectOrNullValue(proto.toObjectOrNull())); fputc('\n', stderr); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 845681ad9d..bbaf785b7e 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -265,6 +265,9 @@ class JSObject : public js::gc::Cell // hasUncacheableProto flag. inline bool hasUncacheableProto() const; bool setUncacheableProto(js::ExclusiveContext* cx) { + MOZ_ASSERT(hasStaticPrototype(), + "uncacheability as a concept is only applicable to static " + "(not dynamically-computed) prototypes"); return setFlags(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE); } @@ -355,20 +358,26 @@ class JSObject : public js::gc::Cell } /* - * We allow the prototype of an object to be lazily computed if the object - * is a proxy. In the lazy case, we store (JSObject*)0x1 in the proto field - * of the object's group. We offer three ways of getting the prototype: + * We permit proxies to dynamically compute their prototype if desired. + * (Not all proxies will so desire: in particular, most DOM proxies can + * track their prototype with a single, nullable JSObject*.) If a proxy + * so desires, we store (JSObject*)0x1 in the proto field of the object's + * group. * - * 1. obj->getProto() returns the prototype, but asserts if obj is a proxy - * with a relevant getPrototype() handler. - * 2. obj->getTaggedProto() returns a TaggedProto, which can be tested to + * We offer three ways to get an object's prototype: + * + * 1. obj->staticPrototype() returns the prototype, but it asserts if obj + * is a proxy, and the proxy has opted to dynamically compute its + * prototype using a getPrototype() handler. + * 2. obj->taggedProto() returns a TaggedProto, which can be tested to * check if the proto is an object, nullptr, or lazily computed. * 3. js::GetPrototype(cx, obj, &proto) computes the proto of an object. - * If obj is a proxy and the proto is lazy, this code may allocate or - * GC in order to compute the proto. Currently, it will not run JS code. + * If obj is a proxy with dynamically-computed prototype, this code may + * perform arbitrary behavior (allocation, GC, run JS) while computing + * the proto. */ - js::TaggedProto getTaggedProto() const { + js::TaggedProto taggedProto() const { return group_->proto(); } @@ -376,37 +385,34 @@ class JSObject : public js::gc::Cell bool uninlinedIsProxy() const; - JSObject* getProto() const { - MOZ_ASSERT(!hasLazyPrototype()); - return getTaggedProto().toObjectOrNull(); + JSObject* staticPrototype() const { + MOZ_ASSERT(hasStaticPrototype()); + return taggedProto().toObjectOrNull(); } - // Normal objects and a subset of proxies have uninteresting [[Prototype]]. - // For such objects the [[Prototype]] is just a value returned when needed - // for accesses, or modified in response to requests. These objects store - // the [[Prototype]] directly within |obj->type_|. - // - // Proxies that don't have such a simple [[Prototype]] instead have a - // "lazy" [[Prototype]]. Accessing the [[Prototype]] of such an object - // requires going through the proxy handler {get,set}Prototype and - // setImmutablePrototype methods. This is most commonly useful for proxies - // that are wrappers around other objects. If the [[Prototype]] of the - // underlying object changes, the [[Prototype]] of the wrapper must also - // simultaneously change. We implement this by having the handler methods - // simply delegate to the wrapped object, forwarding its response to the - // caller. - // - // This method returns true if this object has a non-simple [[Prototype]] - // as described above, or false otherwise. - bool hasLazyPrototype() const { - bool lazy = getTaggedProto().isLazy(); - MOZ_ASSERT_IF(lazy, uninlinedIsProxy()); - return lazy; + // Normal objects and a subset of proxies have an uninteresting, static + // (albeit perhaps mutable) [[Prototype]]. For such objects the + // [[Prototype]] is just a value returned when needed for accesses, or + // modified in response to requests. These objects store the + // [[Prototype]] directly within |obj->group_|. + bool hasStaticPrototype() const { + return !hasDynamicPrototype(); } - // True iff this object's [[Prototype]] is immutable. Must not be called - // on proxies with lazy [[Prototype]]! - inline bool nonLazyPrototypeIsImmutable() const; + // The remaining proxies have a [[Prototype]] requiring dynamic computation + // for every access, going through the proxy handler {get,set}Prototype and + // setImmutablePrototype methods. (Wrappers particularly use this to keep + // the wrapper/wrappee [[Prototype]]s consistent.) + bool hasDynamicPrototype() const { + bool dynamic = taggedProto().isDynamic(); + MOZ_ASSERT_IF(dynamic, uninlinedIsProxy()); + MOZ_ASSERT_IF(dynamic, !isNative()); + return dynamic; + } + + // True iff this object's [[Prototype]] is immutable. Must be called only + // on objects with a static [[Prototype]]! + inline bool staticPrototypeIsImmutable() const; inline void setGroup(js::ObjectGroup* group); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index c607349ee7..a38bdc9375 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -117,7 +117,7 @@ JSObject::setSingleton(js::ExclusiveContext* cx, js::HandleObject obj) MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj)); js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(), - obj->getTaggedProto()); + obj->taggedProto()); if (!group) return false; @@ -152,13 +152,13 @@ JSObject::setGroup(js::ObjectGroup* group) inline bool js::GetPrototype(JSContext* cx, js::HandleObject obj, js::MutableHandleObject protop) { - if (obj->getTaggedProto().isLazy()) { + if (obj->hasDynamicPrototype()) { MOZ_ASSERT(obj->is()); return js::Proxy::getPrototype(cx, obj, protop); - } else { - protop.set(obj->getTaggedProto().toObjectOrNull()); - return true; } + + protop.set(obj->taggedProto().toObjectOrNull()); + return true; } inline bool @@ -487,9 +487,9 @@ JSObject::isIndexed() const } inline bool -JSObject::nonLazyPrototypeIsImmutable() const +JSObject::staticPrototypeIsImmutable() const { - MOZ_ASSERT(!hasLazyPrototype()); + MOZ_ASSERT(hasStaticPrototype()); return hasAllFlags(js::BaseShape::IMMUTABLE_PROTOTYPE); } @@ -564,7 +564,7 @@ ClassMethodIsNative(JSContext* cx, NativeObject* obj, const Class* clasp, jsid m Value v; if (!HasDataProperty(cx, obj, methodid, &v)) { - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, &proto->as(), methodid, &v)) return false; } @@ -585,7 +585,7 @@ HasObjectValueOf(JSObject* obj, JSContext* cx) Value v; while (!HasDataProperty(cx, &obj->as(), valueOf, &v)) { - obj = obj->getProto(); + obj = obj->staticPrototype(); if (!obj || obj->is() || !obj->isNative()) return false; } diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index ec0d392fd6..2d47bf4efa 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -396,14 +396,14 @@ BaseProxyHandler::weakmapKeyDelegate(JSObject* proxy) const bool BaseProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const { - MOZ_CRASH("must override getPrototype with lazy prototype"); + MOZ_CRASH("must override getPrototype with dynamic prototype"); } bool BaseProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result) const { - // Disallow sets of protos on proxies with lazy protos, but no hook. + // Disallow sets of protos on proxies with dynamic prototypes but no hook. // This keeps us away from the footgun of having the first proto set opt // you out of having dynamic protos altogether. JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF, @@ -411,13 +411,6 @@ BaseProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, HandleObject p return false; } -bool -BaseProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, - MutableHandleObject protop) const -{ - MOZ_CRASH("must override getPrototypeIfOrdinary with lazy prototype"); -} - bool BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const { diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index ba5f52bdf9..56e1a661f1 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -183,7 +183,7 @@ js::AppendUnique(JSContext* cx, AutoIdVector& base, AutoIdVector& others) /* static */ bool Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject proto) { - MOZ_ASSERT(proxy->hasLazyPrototype()); + MOZ_ASSERT(proxy->hasDynamicPrototype()); JS_CHECK_RECURSION(cx, return false); return proxy->as().handler()->getPrototype(cx, proxy, proto); } @@ -191,7 +191,7 @@ Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject proto /* static */ bool Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result) { - MOZ_ASSERT(proxy->hasLazyPrototype()); + MOZ_ASSERT(proxy->hasDynamicPrototype()); JS_CHECK_RECURSION(cx, return false); return proxy->as().handler()->setPrototype(cx, proxy, proto, result); } @@ -200,7 +200,6 @@ Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, Objec Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, MutableHandleObject proto) { - MOZ_ASSERT(proxy->hasLazyPrototype()); JS_CHECK_RECURSION(cx, return false); return proxy->as().handler()->getPrototypeIfOrdinary(cx, proxy, isOrdinary, proto); @@ -782,7 +781,7 @@ ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, Value priv) MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this)); MOZ_ASSERT(getClass() == &ProxyObject::proxyClass); MOZ_ASSERT(!IsWindowProxy(this)); - MOZ_ASSERT(hasLazyPrototype()); + MOZ_ASSERT(hasDynamicPrototype()); setHandler(handler); setCrossCompartmentPrivate(priv); diff --git a/js/src/tests/ecma_6/Function/has-instance-jitted.js b/js/src/tests/ecma_6/Function/has-instance-jitted.js new file mode 100644 index 0000000000..a2d33abc7f --- /dev/null +++ b/js/src/tests/ecma_6/Function/has-instance-jitted.js @@ -0,0 +1,96 @@ +const OriginalHasInstance = Function.prototype[Symbol.hasInstance]; + +// Ensure that folding doesn't impact user defined @@hasInstance methods. +{ + function Test() { + this.x = 1; + } + + Object.defineProperty(Test, Symbol.hasInstance, + {writable: true, value: () => false}); + + function x(t) { + return t instanceof Test; + } + + function y() { + let t = new Test; + let b = true; + for (let i = 0; i < 10; i++) { + b = b && x(t); + } + return b; + } + + + function z() { + let f = 0; + let t = 0; + for (let i = 0; i < 100; i++) + assertEq(y(), false); + } + + z(); +} + +// Ensure that the jitting does not clobber user defined @@hasInstance methods. +{ + function a() { + function b() {}; + b.__proto__ = a.prototype; + return b; + }; + let c = new a(); + + let t = 0; + let f = 0; + let e = 0; + for (let i = 0; i < 40000; i++) { + if (i == 20000) + Object.defineProperty(a.prototype, Symbol.hasInstance, + {writable: true, value: () => true}); + if (i == 30000) + Object.setPrototypeOf(c, Function.prototype); + + if (1 instanceof c) { + t++; + } else { + f++; + } + } + + assertEq(t, 10000); + assertEq(f, 30000); +} + +{ + function a() {}; + function b() {}; + Object.defineProperty(a, Symbol.hasInstance, {writable: true, value: () => true}); + assertEq(b instanceof a, true); + for (let _ of Array(10000)) + assertEq(b instanceof a, true); +} + +{ + function a(){}; + function b(){}; + function c(){}; + function d(){}; + function e(){}; + Object.defineProperty(a, Symbol.hasInstance, {value: () => true }); + Object.defineProperty(b, Symbol.hasInstance, {value: () => true }); + Object.defineProperty(c, Symbol.hasInstance, {value: () => true }); + Object.defineProperty(d, Symbol.hasInstance, {value: () => true }); + let funcs = [a, b, c, d]; + for (let f of funcs) + assertEq(e instanceof f, true); + + for (let _ of Array(10001)) { + for (let f of funcs) + assertEq(e instanceof f, true); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Symbol/well-known.js b/js/src/tests/ecma_6/Symbol/well-known.js index 5bad0256be..8c5de12796 100644 --- a/js/src/tests/ecma_6/Symbol/well-known.js +++ b/js/src/tests/ecma_6/Symbol/well-known.js @@ -8,6 +8,7 @@ var names = [ "replace", "search", "species", + "hasInstance", "split", "toPrimitive", "unscopables" diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 3b7ee831c3..79c5df1925 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -782,11 +782,7 @@ js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) RootedValue local(cx, v); if (JSHasInstanceOp hasInstance = clasp->getHasInstance()) return hasInstance(cx, obj, &local, bp); - - RootedValue val(cx, ObjectValue(*obj)); - ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, - JSDVG_SEARCH_STACK, val, nullptr); - return false; + return js::InstanceOfOperator(cx, obj, &local, bp); } static inline bool @@ -3401,7 +3397,8 @@ CASE(JSOP_LAMBDA) JSObject* obj = Lambda(cx, fun, REGS.fp()->scopeChain()); if (!obj) goto error; - MOZ_ASSERT(obj->getProto()); + + MOZ_ASSERT(obj->staticPrototype()); PUSH_OBJECT(*obj); } END_CASE(JSOP_LAMBDA) @@ -3414,7 +3411,8 @@ CASE(JSOP_LAMBDA_ARROW) JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), newTarget); if (!obj) goto error; - MOZ_ASSERT(obj->getProto()); + + MOZ_ASSERT(obj->staticPrototype()); REGS.sp[-1].setObject(*obj); } END_CASE(JSOP_LAMBDA_ARROW) diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 177256834f..05d82b8b92 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -566,7 +566,7 @@ LookupPropertyInline(ExclusiveContext* cx, return true; } - typename MaybeRooted::RootType proto(cx, current->getProto()); + typename MaybeRooted::RootType proto(cx, current->staticPrototype()); if (!proto) break; diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 84c5110b1f..d9d7ec1bec 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1086,7 +1086,7 @@ PurgeProtoChain(ExclusiveContext* cx, JSObject* objArg, HandleId id) if (shape) return obj->as().shadowingShapeChange(cx, *shape); - obj = obj->getProto(); + obj = obj->staticPrototype(); } return true; @@ -1105,7 +1105,7 @@ PurgeScopeChainHelper(ExclusiveContext* cx, HandleObject objArg, HandleId id) if (JSID_IS_INT(id)) return true; - if (!PurgeProtoChain(cx, obj->getProto(), id)) + if (!PurgeProtoChain(cx, obj->staticPrototype(), id)) return false; /* @@ -1646,7 +1646,7 @@ js::NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* // What they all have in common is we do not want to keep walking // the prototype chain, and always claim that the property // doesn't exist. - RootedObject proto(cx, done ? nullptr : pobj->getProto()); + RootedObject proto(cx, done ? nullptr : pobj->staticPrototype()); // Step 8. if (!proto) { @@ -2018,7 +2018,7 @@ NativeGetPropertyInline(JSContext* cx, // being resolved. // What they all have in common is we do not want to keep walking // the prototype chain. - RootedObject proto(cx, done ? nullptr : pobj->getProto()); + RootedObject proto(cx, done ? nullptr : pobj->staticPrototype()); // Step 4.c. The spec algorithm simply returns undefined if proto is // null, but see the comment on GetNonexistentProperty. @@ -2228,9 +2228,10 @@ js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue { MOZ_ASSERT(!obj->is()); - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (proto) return SetProperty(cx, proto, id, v, receiver, result); + return SetPropertyByDefining(cx, id, v, receiver, result); } @@ -2404,7 +2405,7 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handle // being resolved. // What they all have in common is we do not want to keep walking // the prototype chain. - RootedObject proto(cx, done ? nullptr : pobj->getProto()); + RootedObject proto(cx, done ? nullptr : pobj->staticPrototype()); if (!proto) { // Step 4.d.i (and step 5). return SetNonexistentProperty(cx, id, v, receiver, qualified, result); diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 6200fd4c52..2c2788f2d9 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -260,7 +260,7 @@ JSObject::shouldSplicePrototype(JSContext* cx) * object if their __proto__ had previously been set to null, as this * will change the prototype for all other objects with the same type. */ - if (getProto() != nullptr) + if (staticPrototype() != nullptr) return false; return isSingleton(); } @@ -320,7 +320,7 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj) if (obj->is() && obj->as().length() > INT32_MAX) initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; - Rooted proto(cx, obj->getTaggedProto()); + Rooted proto(cx, obj->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto, initialFlags); if (!group) @@ -488,7 +488,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, } ObjectGroupFlags initialFlags = 0; - if (proto.isLazy() || (proto.isObject() && proto.toObject()->isNewGroupUnknown())) + if (proto.isDynamic() || (proto.isObject() && proto.toObject()->isNewGroupUnknown())) initialFlags = OBJECT_FLAG_DYNAMIC_MASK; Rooted protoRoot(cx, proto); @@ -1208,7 +1208,7 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_ // default (which will have unknown properties) so that the group we // just created will be collected by the GC. if (obj->slotSpan() != nproperties) { - ObjectGroup* group = defaultNewGroup(cx, obj->getClass(), obj->getTaggedProto()); + ObjectGroup* group = defaultNewGroup(cx, obj->getClass(), obj->taggedProto()); if (!group) return nullptr; obj->setGroup(group); diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index 9c5dd39f3d..82befe49c4 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -99,6 +99,10 @@ class ObjectGroup : public gc::TenuredCell clasp_ = clasp; } + bool hasDynamicPrototype() const { + return proto_.isDynamic(); + } + const HeapPtr& proto() const { return proto_; } diff --git a/js/src/vm/PIC.cpp b/js/src/vm/PIC.cpp index 86da0b9e79..587a80cd0d 100644 --- a/js/src/vm/PIC.cpp +++ b/js/src/vm/PIC.cpp @@ -181,14 +181,7 @@ bool js::ForOfPIC::Chain::isOptimizableArray(JSObject* obj) { MOZ_ASSERT(obj->is()); - - // Ensure object's prototype is the actual Array.prototype - if (!obj->getTaggedProto().isObject()) - return false; - if (obj->getTaggedProto().toObject() != arrayProto_) - return false; - - return true; + return obj->staticPrototype() == arrayProto_; } bool diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 9b7f42c5e4..c15fcccbe9 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -849,7 +849,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx) return matchResultTemplateObject_; // = nullptr // Create a new group for the template. - Rooted proto(cx, templateObject->getTaggedProto()); + Rooted proto(cx, templateObject->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto); if (!group) return matchResultTemplateObject_; // = nullptr diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 19ae97f5b7..6dc765b374 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -2003,6 +2003,12 @@ class DebugScopeProxy : public BaseProxyHandler return isFunctionScope(scope) && !scope.as().callee().hasLexicalThis(); } + bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, + MutableHandleObject protop) const override + { + MOZ_CRASH("shouldn't be possible to access the prototype chain of a DebugScopeProxy"); + } + bool preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const override { diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index d41eb8f0b3..b2e1bcfab9 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -897,7 +897,7 @@ class NestedScopeObject : public ScopeObject public: // Return the static scope corresponding to this scope chain object. inline NestedStaticScope* staticScope() { - return &getProto()->as(); + return &staticPrototype()->as(); } void initEnclosingScope(JSObject* obj) { @@ -927,7 +927,7 @@ class DynamicWithObject : public NestedScopeObject WithKind kind = SyntacticWith); StaticWithScope& staticWith() const { - return getProto()->as(); + return staticPrototype()->as(); } /* Return the 'o' in 'with (o)'. */ @@ -1007,7 +1007,7 @@ class ClonedBlockObject : public NestedScopeObject public: /* The static block from which this block was cloned. */ StaticBlockScope& staticBlock() const { - return getProto()->as(); + return staticPrototype()->as(); } /* Assuming 'put' has been called, return the value of the ith let var. */ @@ -1387,7 +1387,7 @@ template<> inline bool JSObject::is() const { - return hasClass(&js::ClonedBlockObject::class_) && !getProto(); + return hasClass(&js::ClonedBlockObject::class_) && !staticPrototype(); } template<> @@ -1411,7 +1411,7 @@ template<> inline bool JSObject::is() const { - return hasClass(&js::ClonedBlockObject::class_) && !!getProto(); + return hasClass(&js::ClonedBlockObject::class_) && staticPrototype(); } template<> diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 40bcb7df59..3678f89768 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -479,7 +479,7 @@ intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp) if (!GetPrototype(cx, targetObj, &proto)) return false; - if (bound->getProto() != proto) { + if (bound->staticPrototype() != proto) { if (!SetPrototype(cx, bound, proto)) return false; } diff --git a/js/src/vm/Shape-inl.h b/js/src/vm/Shape-inl.h index 92e0b9f54a..e44459f110 100644 --- a/js/src/vm/Shape-inl.h +++ b/js/src/vm/Shape-inl.h @@ -134,7 +134,7 @@ EmptyShape::ensureInitialCustomShape(ExclusiveContext* cx, HandlegetProto()); + RootedObject proto(cx, obj->staticPrototype()); EmptyShape::insertInitialShape(cx, shape, proto); return true; } diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index be919c5c91..50b2bbf031 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1194,7 +1194,7 @@ JSObject::setFlags(ExclusiveContext* cx, BaseShape::Flag flags, GenerateShape ge if (!existingShape) return false; - Shape* newShape = Shape::setObjectFlags(cx, flags, self->getTaggedProto(), existingShape); + Shape* newShape = Shape::setObjectFlags(cx, flags, self->taggedProto(), existingShape); if (!newShape) return false; diff --git a/js/src/vm/TaggedProto.cpp b/js/src/vm/TaggedProto.cpp index a7c79aff27..bde4a50576 100644 --- a/js/src/vm/TaggedProto.cpp +++ b/js/src/vm/TaggedProto.cpp @@ -49,7 +49,7 @@ js::TaggedProto::hashCode() const uint64_t js::TaggedProto::uniqueId() const { - if (isLazy()) + if (isDynamic()) return uint64_t(1); JSObject* obj = toObjectOrNull(); if (!obj) diff --git a/js/src/vm/TaggedProto.h b/js/src/vm/TaggedProto.h index 675b21ddac..cad3248221 100644 --- a/js/src/vm/TaggedProto.h +++ b/js/src/vm/TaggedProto.h @@ -24,7 +24,7 @@ class TaggedProto uintptr_t toWord() const { return uintptr_t(proto); } - bool isLazy() const { + bool isDynamic() const { return proto == LazyProto; } bool isObject() const { @@ -83,7 +83,7 @@ class TaggedProtoOperations public: uintptr_t toWord() const { return value().toWord(); } - inline bool isLazy() const { return value().isLazy(); } + inline bool isDynamic() const { return value().isDynamic(); } inline bool isObject() const { return value().isObject(); } inline JSObject* toObject() const { return value().toObject(); } inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); } diff --git a/js/src/vm/TraceLoggingGraph.cpp b/js/src/vm/TraceLoggingGraph.cpp index 262d16a5bf..88cfbb8f7c 100644 --- a/js/src/vm/TraceLoggingGraph.cpp +++ b/js/src/vm/TraceLoggingGraph.cpp @@ -6,6 +6,13 @@ #include "vm/TraceLoggingGraph.h" +#ifdef XP_WIN +#include +#define getpid _getpid +#else +#include +#endif + #include "mozilla/Endian.h" #include "jsstr.h" @@ -28,12 +35,23 @@ TraceLoggerGraphState* traceLoggerGraphState = nullptr; bool TraceLoggerGraphState::init() { - out = fopen(TRACE_LOG_DIR "tl-data.json", "w"); + pid_ = (uint32_t) getpid(); + + char filename[sizeof TRACE_LOG_DIR "tl-data.4294967295.json"]; + sprintf(filename, TRACE_LOG_DIR "tl-data.%u.json", pid_); + out = fopen(filename, "w"); if (!out) return false; fprintf(out, "["); + // Write the last tl-data.*.json file to tl-data.json. + // In most cases that is the wanted file. + if (FILE* last = fopen(TRACE_LOG_DIR "tl-data.json", "w")) { + fprintf(last, "\"tl-data.%u.json\"", pid_); + fclose(last); + } + #ifdef DEBUG initialized = true; #endif @@ -73,9 +91,9 @@ TraceLoggerGraphState::nextLoggerId() } } - int written = fprintf(out, "{\"tree\":\"tl-tree.%d.tl\", \"events\":\"tl-event.%d.tl\", " - "\"dict\":\"tl-dict.%d.json\", \"treeFormat\":\"64,64,31,1,32\"}", - numLoggers, numLoggers, numLoggers); + int written = fprintf(out, "{\"tree\":\"tl-tree.%u.%d.tl\", \"events\":\"tl-event.%u.%d.tl\", " + "\"dict\":\"tl-dict.%u.%d.json\", \"treeFormat\":\"64,64,31,1,32\"}", + pid_, numLoggers, pid_, numLoggers, pid_, numLoggers); if (written < 0) { fprintf(stderr, "TraceLogging: Error while writing.\n"); return uint32_t(-1); @@ -134,16 +152,18 @@ TraceLoggerGraph::init(uint64_t startTimestamp) return false; } - char dictFilename[sizeof TRACE_LOG_DIR "tl-dict.100.json"]; - sprintf(dictFilename, TRACE_LOG_DIR "tl-dict.%d.json", loggerId); + uint32_t pid = traceLoggerGraphState->pid(); + + char dictFilename[sizeof TRACE_LOG_DIR "tl-dict.4294967295.100.json"]; + sprintf(dictFilename, TRACE_LOG_DIR "tl-dict.%u.%d.json", pid, loggerId); dictFile = fopen(dictFilename, "w"); if (!dictFile) { failed = true; return false; } - char treeFilename[sizeof TRACE_LOG_DIR "tl-tree.100.tl"]; - sprintf(treeFilename, TRACE_LOG_DIR "tl-tree.%d.tl", loggerId); + char treeFilename[sizeof TRACE_LOG_DIR "tl-tree.4294967295.100.tl"]; + sprintf(treeFilename, TRACE_LOG_DIR "tl-tree.%u.%d.tl", pid, loggerId); treeFile = fopen(treeFilename, "w+b"); if (!treeFile) { fclose(dictFile); @@ -152,8 +172,8 @@ TraceLoggerGraph::init(uint64_t startTimestamp) return false; } - char eventFilename[sizeof TRACE_LOG_DIR "tl-event.100.tl"]; - sprintf(eventFilename, TRACE_LOG_DIR "tl-event.%d.tl", loggerId); + char eventFilename[sizeof TRACE_LOG_DIR "tl-event.4294967295.100.tl"]; + sprintf(eventFilename, TRACE_LOG_DIR "tl-event.%u.%d.tl", pid, loggerId); eventFile = fopen(eventFilename, "wb"); if (!eventFile) { fclose(dictFile); @@ -570,3 +590,5 @@ TraceLoggerGraph::addTextId(uint32_t id, const char* text) if (!js::FileEscapedString(dictFile, text, strlen(text), '"')) failed = true; } + +#undef getpid diff --git a/js/src/vm/TraceLoggingGraph.h b/js/src/vm/TraceLoggingGraph.h index f9147531f5..a1430e215f 100644 --- a/js/src/vm/TraceLoggingGraph.h +++ b/js/src/vm/TraceLoggingGraph.h @@ -68,6 +68,7 @@ void DestroyTraceLoggerGraphState(); class TraceLoggerGraphState { uint32_t numLoggers; + uint32_t pid_; // File pointer to the "tl-data.json" file. (Explained above). FILE* out; @@ -81,8 +82,9 @@ class TraceLoggerGraphState public: TraceLoggerGraphState() - : numLoggers(0) - , out(nullptr) + : numLoggers(0), + pid_(0), + out(nullptr) #ifdef DEBUG , initialized(false) #endif @@ -92,6 +94,7 @@ class TraceLoggerGraphState ~TraceLoggerGraphState(); uint32_t nextLoggerId(); + uint32_t pid() { return pid_; } }; class TraceLoggerGraph diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 7995ee1d7c..558955392f 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -1215,7 +1215,7 @@ TypeSet::ObjectKey::clasp() TaggedProto TypeSet::ObjectKey::proto() { - return isGroup() ? group()->proto() : singleton()->getTaggedProto(); + return isGroup() ? group()->proto() : singleton()->taggedProto(); } TypeNewScript* @@ -2422,7 +2422,7 @@ TemporaryTypeSet::getCommonPrototype(CompilerConstraintList* constraints, JSObje TaggedProto nproto = key->proto(); if (isFirst) { - if (nproto.isLazy()) + if (nproto.isDynamic()) return false; *proto = nproto.toObjectOrNull(); isFirst = false; @@ -2486,7 +2486,7 @@ PrototypeHasIndexedProperty(CompilerConstraintList* constraints, JSObject* obj) HeapTypeSetKey index = key->property(JSID_VOID); if (index.nonData(constraints) || index.isOwnProperty(constraints)) return true; - obj = obj->getProto(); + obj = obj->staticPrototype(); } while (obj); return false; @@ -3052,8 +3052,11 @@ ObjectGroup::print() TaggedProto tagged(proto()); fprintf(stderr, "%s : %s", TypeSet::ObjectGroupString(this), - tagged.isObject() ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject())) - : (tagged.isLazy() ? "(lazy)" : "(null)")); + tagged.isObject() + ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject())) + : tagged.isDynamic() + ? "(dynamic)" + : "(null)"); if (unknownProperties()) { fprintf(stderr, " unknown"); @@ -3164,7 +3167,7 @@ js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* gr return false; if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_(group))) return false; - proto = proto->getProto(); + proto = proto->staticPrototype(); } return true; } @@ -3364,7 +3367,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun, if (!setSingleton(cx, fun)) return false; } else { - RootedObject funProto(cx, fun->getProto()); + RootedObject funProto(cx, fun->staticPrototype()); Rooted taggedProto(cx, TaggedProto(funProto)); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_, taggedProto); @@ -3661,9 +3664,7 @@ ChangeObjectFixedSlotCount(JSContext* cx, PlainObject* obj, gc::AllocKind allocK { MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty())); - Shape* newShape = ReshapeForAllocKind(cx, obj->lastProperty(), - obj->getTaggedProto(), - allocKind); + Shape* newShape = ReshapeForAllocKind(cx, obj->lastProperty(), obj->taggedProto(), allocKind); if (!newShape) return false; diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index bf6e492589..0fb869f58f 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -727,7 +727,7 @@ UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj, return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); @@ -778,7 +778,7 @@ UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; @@ -805,7 +805,7 @@ UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; @@ -1428,7 +1428,7 @@ UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj, return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); @@ -1479,7 +1479,7 @@ UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; @@ -1500,7 +1500,7 @@ UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index d3366b31d0..c3d4e920df 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -214,7 +214,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 gPrototypeProperties['Function'] = ["constructor", "toSource", "toString", "apply", "call", "bind", - "isGenerator", "length", "name", "arguments", "caller"]; + "isGenerator", "length", "name", "arguments", "caller", Symbol.hasInstance]; gConstructorProperties['Function'] = constructorProps([]) gPrototypeProperties['RegExp'] = @@ -579,7 +579,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 trickyArray[1] = "hi"; is(trickyArray.length, 0, "Length remains non-writable"); is(trickyArray[1], undefined, "Frozen length forbids new properties"); - + is(trickyArray instanceof iwin.Array, true, "instanceof should work across xray wrappers."); testTrickyObject(trickyArray); testArrayIterators(new iwin.Array(1, 1, 2, 3, 5), [1, 1, 2, 3, 5]); diff --git a/layout/base/ZoomConstraintsClient.cpp b/layout/base/ZoomConstraintsClient.cpp index ef1db18081..c76f971c39 100644 --- a/layout/base/ZoomConstraintsClient.cpp +++ b/layout/base/ZoomConstraintsClient.cpp @@ -152,7 +152,7 @@ ZoomConstraintsClient::Observe(nsISupports* aSubject, const char* aTopic, const // We need to run this later because all the pref change listeners need // to execute before we can be guaranteed that gfxPrefs::ForceUserScalable() // returns the updated value. - NS_DispatchToMainThread(NS_NewRunnableMethod( + NS_DispatchToMainThread(NewRunnableMethod( this, &ZoomConstraintsClient::RefreshZoomConstraints)); } return NS_OK; diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 4f7771913b..80590ff9e5 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1588,7 +1588,7 @@ nsPresContext::ThemeChanged() sThemeChanged = true; nsCOMPtr ev = - NS_NewRunnableMethod(this, &nsPresContext::ThemeChangedInternal); + NewRunnableMethod(this, &nsPresContext::ThemeChangedInternal); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { mPendingThemeChanged = true; } @@ -1647,7 +1647,7 @@ nsPresContext::SysColorChanged() if (!mPendingSysColorChanged) { sLookAndFeelChanged = true; nsCOMPtr ev = - NS_NewRunnableMethod(this, &nsPresContext::SysColorChangedInternal); + NewRunnableMethod(this, &nsPresContext::SysColorChangedInternal); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { mPendingSysColorChanged = true; } @@ -1682,7 +1682,7 @@ nsPresContext::UIResolutionChanged() { if (!mPendingUIResolutionChanged) { nsCOMPtr ev = - NS_NewRunnableMethod(this, &nsPresContext::UIResolutionChangedInternal); + NewRunnableMethod(this, &nsPresContext::UIResolutionChangedInternal); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { mPendingUIResolutionChanged = true; } @@ -1933,7 +1933,7 @@ nsPresContext::PostMediaFeatureValuesChangedEvent() // need to track whether it's been added). if (!mPendingMediaFeatureValuesChanged) { nsCOMPtr ev = - NS_NewRunnableMethod(this, &nsPresContext::HandleMediaFeatureValuesChangedEvent); + NewRunnableMethod(this, &nsPresContext::HandleMediaFeatureValuesChangedEvent); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { mPendingMediaFeatureValuesChanged = true; mDocument->SetNeedStyleFlush(); @@ -2125,7 +2125,7 @@ nsPresContext::RebuildCounterStyles() mDocument->SetNeedStyleFlush(); if (!mPostedFlushCounterStyles) { nsCOMPtr ev = - NS_NewRunnableMethod(this, &nsPresContext::HandleRebuildCounterStyles); + NewRunnableMethod(this, &nsPresContext::HandleRebuildCounterStyles); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { mPostedFlushCounterStyles = true; } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 4fec46d341..ada85e8cae 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1918,7 +1918,7 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight) } } else { RefPtr > resizeEvent = - NS_NewRunnableMethod(this, &PresShell::FireResizeEvent); + NewRunnableMethod(this, &PresShell::FireResizeEvent); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) { mResizeEvent = resizeEvent; mDocument->SetNeedStyleFlush(); @@ -6100,7 +6100,7 @@ PresShell::ScheduleApproximateFrameVisibilityUpdateNow() } RefPtr > ev = - NS_NewRunnableMethod(this, &PresShell::UpdateApproximateFrameVisibility); + NewRunnableMethod(this, &PresShell::UpdateApproximateFrameVisibility); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { mUpdateApproximateFrameVisibilityEvent = ev; } @@ -6383,7 +6383,7 @@ PresShell::Paint(nsView* aViewToPaint, // since this is happening during a paint and updating the visible // regions triggers a recomposite. RefPtr> event = - NS_NewRunnableMethod(this, &PresShell::NotifyCompositorOfVisibleRegionsChange); + NewRunnableMethod(this, &PresShell::NotifyCompositorOfVisibleRegionsChange); if (NS_SUCCEEDED(NS_DispatchToMainThread(event))) { mNotifyCompositorOfVisibleRegionsChangeEvent = event; } diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 9799b36e85..882882501f 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -420,9 +420,9 @@ private: } nsCOMPtr vsyncEvent = - NS_NewRunnableMethodWithArg(this, - &RefreshDriverVsyncObserver::TickRefreshDriver, - aVsyncTimestamp); + NewRunnableMethod(this, + &RefreshDriverVsyncObserver::TickRefreshDriver, + aVsyncTimestamp); NS_DispatchToMainThread(vsyncEvent); } else { RefPtr kungFuDeathGrip(this); @@ -1980,7 +1980,7 @@ nsRefreshDriver::Thaw() // updates our mMostRecentRefresh, but the DoRefresh call won't run // and notify our observers until we get back to the event loop. // Thus MostRecentRefresh() will lie between now and the DoRefresh. - NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsRefreshDriver::DoRefresh)); + NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsRefreshDriver::DoRefresh)); EnsureTimerStarted(); } } diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index a0bf2519ef..8c351efa6d 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -129,6 +129,7 @@ using namespace mozilla::system; #include "MediaDecoder.h" #include "mozilla/layers/CompositorLRU.h" #include "mozilla/dom/devicestorage/DeviceStorageStatics.h" +#include "mozilla/ServoBindings.h" #include "mozilla/StaticPresData.h" #ifdef MOZ_B2G_BT @@ -317,6 +318,10 @@ nsLayoutStatics::Initialize() mozilla::dom::WebCryptoThreadPool::Initialize(); +#ifdef MOZ_STYLO + Servo_Initialize(); +#endif + return NS_OK; } diff --git a/layout/ipc/VsyncParent.cpp b/layout/ipc/VsyncParent.cpp index 9fdb8511d6..6de114f603 100644 --- a/layout/ipc/VsyncParent.cpp +++ b/layout/ipc/VsyncParent.cpp @@ -50,9 +50,9 @@ VsyncParent::NotifyVsync(TimeStamp aTimeStamp) // Called on hardware vsync thread. We should post to current ipc thread. MOZ_ASSERT(!IsOnBackgroundThread()); nsCOMPtr vsyncEvent = - NS_NewRunnableMethodWithArg(this, - &VsyncParent::DispatchVsyncEvent, - aTimeStamp); + NewRunnableMethod(this, + &VsyncParent::DispatchVsyncEvent, + aTimeStamp); MOZ_ALWAYS_SUCCEEDS(mBackgroundThread->Dispatch(vsyncEvent, NS_DISPATCH_NORMAL)); return true; } diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp index 14c14c4800..5822030aa8 100644 --- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -1473,7 +1473,7 @@ FontFaceSet::OnFontFaceStatusChanged(FontFace* aFontFace) if (!mDelayedLoadCheck) { mDelayedLoadCheck = true; nsCOMPtr checkTask = - NS_NewRunnableMethod(this, &FontFaceSet::CheckLoadingFinishedAfterDelay); + NewRunnableMethod(this, &FontFaceSet::CheckLoadingFinishedAfterDelay); NS_DispatchToMainThread(checkTask); } } diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index f34583f3ff..b9c1cbd03d 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -296,6 +296,13 @@ Servo_ReleaseComputedValues(ServoComputedValues*) "non-MOZ_STYLO build"); } +void +Servo_Initialize() +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_Initialize in a " + "non-MOZ_STYLO build"); +} + void Servo_RestyleDocument(RawGeckoDocument* doc, RawServoStyleSet* set) { diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index a96ac15851..1ceb01ad34 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -93,7 +93,10 @@ ServoComputedValues* Servo_GetComputedValuesForPseudoElement(ServoComputedValues void Servo_AddRefComputedValues(ServoComputedValues*); void Servo_ReleaseComputedValues(ServoComputedValues*); -// Servo API. +// Initialize Servo components. Should be called exactly once at startup. +void Servo_Initialize(); + +// Restyle the given document. void Servo_RestyleDocument(RawGeckoDocument* doc, RawServoStyleSet* set); // Style-struct management. diff --git a/layout/xul/tree/nsTreeColumns.cpp b/layout/xul/tree/nsTreeColumns.cpp index 45e5a8adaf..911cae0956 100644 --- a/layout/xul/tree/nsTreeColumns.cpp +++ b/layout/xul/tree/nsTreeColumns.cpp @@ -606,12 +606,6 @@ nsTreeColumns::NamedGetter(const nsAString& aId, bool& aFound) return nullptr; } -bool -nsTreeColumns::NameIsEnumerable(const nsAString& aName) -{ - return true; -} - nsTreeColumn* nsTreeColumns::GetNamedColumn(const nsAString& aId) { @@ -627,7 +621,7 @@ nsTreeColumns::GetNamedColumn(const nsAString& aId, nsITreeColumn** _retval) } void -nsTreeColumns::GetSupportedNames(unsigned, nsTArray& aNames) +nsTreeColumns::GetSupportedNames(nsTArray& aNames) { for (nsTreeColumn* currCol = mFirstColumn; currCol; currCol = currCol->GetNext()) { aNames.AppendElement(currCol->GetId()); diff --git a/layout/xul/tree/nsTreeColumns.h b/layout/xul/tree/nsTreeColumns.h index 54fed19663..3cbc4db085 100644 --- a/layout/xul/tree/nsTreeColumns.h +++ b/layout/xul/tree/nsTreeColumns.h @@ -186,9 +186,8 @@ public: nsTreeColumn* IndexedGetter(uint32_t aIndex, bool& aFound); nsTreeColumn* GetColumnAt(uint32_t aIndex); nsTreeColumn* NamedGetter(const nsAString& aId, bool& aFound); - bool NameIsEnumerable(const nsAString& aName); nsTreeColumn* GetNamedColumn(const nsAString& aId); - void GetSupportedNames(unsigned, nsTArray& aNames); + void GetSupportedNames(nsTArray& aNames); // Uses XPCOM InvalidateColumns(). // Uses XPCOM RestoreNaturalOrder(). diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index c262fab714..618858ae46 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -192,7 +192,7 @@ public: ++mLength; // Atomic nsCOMPtr runnable = - NS_NewRunnableMethodWithArgs, bool>( + NewRunnableMethod, bool>( this, &VideoFrameConverter::ProcessVideoFrame, aChunk.mFrame.GetImage(), forceBlack); mTaskQueue->Dispatch(runnable.forget()); diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp index 8060d70891..e6b1e5f849 100644 --- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -1223,11 +1223,10 @@ nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx, if (NS_IsMainThread()) { FireOnProgress(offset + count); } else { - nsCOMPtr runnable = - NS_NewRunnableMethodWithArg(this, - &nsJARChannel::FireOnProgress, - offset + count); - NS_DispatchToMainThread(runnable); + NS_DispatchToMainThread(NewRunnableMethod + (this, + &nsJARChannel::FireOnProgress, + offset + count)); } } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index be84fa55e0..4d45233334 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -535,8 +535,6 @@ pref("media.encoder.omx.enabled", true); // Whether to autostart a media element with an |autoplay| attribute pref("media.autoplay.enabled", true); -// Whether to autostart a media element with an autoplaying script event -pref("media.autoplay.allowscripted", true); // The default number of decoded video frames that are enqueued in // MediaDecoderReader's mVideoQueue. diff --git a/netwerk/base/BackgroundFileSaver.cpp b/netwerk/base/BackgroundFileSaver.cpp index 87d5691bf6..1e27ccd35f 100644 --- a/netwerk/base/BackgroundFileSaver.cpp +++ b/netwerk/base/BackgroundFileSaver.cpp @@ -323,11 +323,9 @@ BackgroundFileSaver::GetWorkerThreadAttention(bool aShouldInterruptCopy) if (!mAsyncCopyContext) { // Copy is not in progress, post an event to handle the change manually. - nsCOMPtr event = - NS_NewRunnableMethod(this, &BackgroundFileSaver::ProcessAttention); - NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); - - rv = mWorkerThread->Dispatch(event, NS_DISPATCH_NORMAL); + rv = mWorkerThread->Dispatch(NewRunnableMethod(this, + &BackgroundFileSaver::ProcessAttention), + NS_DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, rv); } else if (aShouldInterruptCopy) { // Interrupt the copy. The copy will be resumed, if needed, by the @@ -745,10 +743,9 @@ BackgroundFileSaver::CheckCompletion() } // Post an event to notify that the operation completed. - nsCOMPtr event = - NS_NewRunnableMethod(this, &BackgroundFileSaver::NotifySaveComplete); - if (!event || - NS_FAILED(mControlThread->Dispatch(event, NS_DISPATCH_NORMAL))) { + if (NS_FAILED(mControlThread->Dispatch(NewRunnableMethod(this, + &BackgroundFileSaver::NotifySaveComplete), + NS_DISPATCH_NORMAL))) { NS_WARNING("Unable to post completion event to the control thread."); } @@ -1142,10 +1139,9 @@ BackgroundFileSaverStreamListener::AsyncCopyProgressCallback(void *aClosure, self->mReceivedTooMuchData = false; // Post an event to verify if the request should be resumed. - nsCOMPtr event = NS_NewRunnableMethod(self, - &BackgroundFileSaverStreamListener::NotifySuspendOrResume); - if (!event || NS_FAILED(self->mControlThread->Dispatch(event, - NS_DISPATCH_NORMAL))) { + if (NS_FAILED(self->mControlThread->Dispatch(NewRunnableMethod(self, + &BackgroundFileSaverStreamListener::NotifySuspendOrResume), + NS_DISPATCH_NORMAL))) { NS_WARNING("Unable to post resume event to the control thread."); } } diff --git a/netwerk/base/Dashboard.cpp b/netwerk/base/Dashboard.cpp index cf180dc1eb..d084f75a1f 100644 --- a/netwerk/base/Dashboard.cpp +++ b/netwerk/base/Dashboard.cpp @@ -175,10 +175,9 @@ ConnectionData::OnTransportStatus(nsITransport *aTransport, nsresult aStatus, } GetErrorString(aStatus, mStatus); - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (mDashboard, &Dashboard::GetConnectionStatus, this); - mThread->Dispatch(event, NS_DISPATCH_NORMAL); + mThread->Dispatch(NewRunnableMethod> + (mDashboard, &Dashboard::GetConnectionStatus, this), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -197,10 +196,9 @@ ConnectionData::Notify(nsITimer *aTimer) mTimer = nullptr; mStatus.AssignLiteral(MOZ_UTF16("NS_ERROR_NET_TIMEOUT")); - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (mDashboard, &Dashboard::GetConnectionStatus, this); - mThread->Dispatch(event, NS_DISPATCH_NORMAL); + mThread->Dispatch(NewRunnableMethod> + (mDashboard, &Dashboard::GetConnectionStatus, this), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -287,10 +285,9 @@ LookupHelper::OnLookupComplete(nsICancelable *aRequest, mStatus = aStatus; RefPtr arg = new LookupArgument(aRecord, this); - nsCOMPtr event = - NS_NewRunnableMethodWithArg >( - this, &LookupHelper::ConstructAnswer, arg); - mThread->Dispatch(event, NS_DISPATCH_NORMAL); + mThread->Dispatch(NewRunnableMethod> + (this, &LookupHelper::ConstructAnswer, arg), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -354,10 +351,9 @@ Dashboard::RequestSockets(NetDashboardCallback *aCallback) socketData->mCallback = new nsMainThreadPtrHolder(aCallback, true); socketData->mThread = NS_GetCurrentThread(); - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (this, &Dashboard::GetSocketsDispatch, socketData); - gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); + gSocketTransportService->Dispatch(NewRunnableMethod> + (this, &Dashboard::GetSocketsDispatch, socketData), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -370,10 +366,9 @@ Dashboard::GetSocketsDispatch(SocketData *aSocketData) socketData->mTotalSent = gSocketTransportService->GetSentBytes(); socketData->mTotalRecv = gSocketTransportService->GetReceivedBytes(); } - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (this, &Dashboard::GetSockets, socketData); - socketData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + socketData->mThread->Dispatch(NewRunnableMethod> + (this, &Dashboard::GetSockets, socketData), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -426,10 +421,9 @@ Dashboard::RequestHttpConnections(NetDashboardCallback *aCallback) new nsMainThreadPtrHolder(aCallback, true); httpData->mThread = NS_GetCurrentThread(); - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (this, &Dashboard::GetHttpDispatch, httpData); - gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); + gSocketTransportService->Dispatch(NewRunnableMethod> + (this, &Dashboard::GetHttpDispatch, httpData), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -438,10 +432,9 @@ Dashboard::GetHttpDispatch(HttpData *aHttpData) { RefPtr httpData = aHttpData; HttpInfo::GetHttpConnectionData(&httpData->mData); - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (this, &Dashboard::GetHttpConnections, httpData); - httpData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + httpData->mThread->Dispatch(NewRunnableMethod> + (this, &Dashboard::GetHttpConnections, httpData), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -604,10 +597,9 @@ Dashboard::RequestWebsocketConnections(NetDashboardCallback *aCallback) new nsMainThreadPtrHolder(aCallback, true); wsRequest->mThread = NS_GetCurrentThread(); - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (this, &Dashboard::GetWebSocketConnections, wsRequest); - wsRequest->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + wsRequest->mThread->Dispatch(NewRunnableMethod> + (this, &Dashboard::GetWebSocketConnections, wsRequest), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -666,10 +658,9 @@ Dashboard::RequestDNSInfo(NetDashboardCallback *aCallback) } } - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (this, &Dashboard::GetDnsInfoDispatch, dnsData); - gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); + gSocketTransportService->Dispatch(NewRunnableMethod> + (this, &Dashboard::GetDnsInfoDispatch, dnsData), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -680,10 +671,9 @@ Dashboard::GetDnsInfoDispatch(DnsData *aDnsData) if (mDnsService) { mDnsService->GetDNSCacheEntries(&dnsData->mData); } - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (this, &Dashboard::GetDNSCacheEntries, dnsData); - dnsData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + dnsData->mThread->Dispatch(NewRunnableMethod> + (this, &Dashboard::GetDNSCacheEntries, dnsData), + NS_DISPATCH_NORMAL); return NS_OK; } @@ -810,10 +800,9 @@ Dashboard::RequestConnection(const nsACString& aHost, uint32_t aPort, rv = TestNewConnection(connectionData); if (NS_FAILED(rv)) { mozilla::net::GetErrorString(rv, connectionData->mStatus); - nsCOMPtr event = - NS_NewRunnableMethodWithArg > - (this, &Dashboard::GetConnectionStatus, connectionData); - connectionData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); + connectionData->mThread->Dispatch(NewRunnableMethod> + (this, &Dashboard::GetConnectionStatus, connectionData), + NS_DISPATCH_NORMAL); return rv; } diff --git a/netwerk/base/OfflineObserver.cpp b/netwerk/base/OfflineObserver.cpp index 1cdc838ea7..286173e98e 100644 --- a/netwerk/base/OfflineObserver.cpp +++ b/netwerk/base/OfflineObserver.cpp @@ -21,9 +21,8 @@ OfflineObserver::RegisterOfflineObserver() if (NS_IsMainThread()) { RegisterOfflineObserverMainThread(); } else { - nsCOMPtr event = - NS_NewRunnableMethod(this, &OfflineObserver::RegisterOfflineObserverMainThread); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(this, + &OfflineObserver::RegisterOfflineObserverMainThread)); } } @@ -33,9 +32,8 @@ OfflineObserver::RemoveOfflineObserver() if (NS_IsMainThread()) { RemoveOfflineObserverMainThread(); } else { - nsCOMPtr event = - NS_NewRunnableMethod(this, &OfflineObserver::RemoveOfflineObserverMainThread); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(this, + &OfflineObserver::RemoveOfflineObserverMainThread)); } } diff --git a/netwerk/base/Tickler.cpp b/netwerk/base/Tickler.cpp index 5d42440dca..a705104ba3 100644 --- a/netwerk/base/Tickler.cpp +++ b/netwerk/base/Tickler.cpp @@ -130,7 +130,7 @@ void Tickler::Tickle() void Tickler::PostCheckTickler() { mLock.AssertCurrentThreadOwns(); - mThread->Dispatch(NS_NewRunnableMethod(this, &Tickler::CheckTickler), + mThread->Dispatch(NewRunnableMethod(this, &Tickler::CheckTickler), NS_DISPATCH_NORMAL); return; } @@ -146,7 +146,7 @@ void Tickler::MaybeStartTickler() mLock.AssertCurrentThreadOwns(); if (!NS_IsMainThread()) { NS_DispatchToMainThread( - NS_NewRunnableMethod(this, &Tickler::MaybeStartTicklerUnlocked)); + NewRunnableMethod(this, &Tickler::MaybeStartTicklerUnlocked)); return; } diff --git a/netwerk/base/nsAsyncStreamCopier.cpp b/netwerk/base/nsAsyncStreamCopier.cpp index 331dcc65a9..0a1473a9fe 100644 --- a/netwerk/base/nsAsyncStreamCopier.cpp +++ b/netwerk/base/nsAsyncStreamCopier.cpp @@ -44,8 +44,9 @@ public: return NS_OK; } - nsCOMPtr event = NS_NewRunnableMethod(mCopier, &nsAsyncStreamCopier::AsyncCopyInternal); - rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL); + rv = mTarget->Dispatch(NewRunnableMethod(mCopier, + &nsAsyncStreamCopier::AsyncCopyInternal), + NS_DISPATCH_NORMAL); MOZ_ASSERT(NS_SUCCEEDED(rv)); if (NS_FAILED(rv)) { diff --git a/netwerk/base/nsInputStreamPump.cpp b/netwerk/base/nsInputStreamPump.cpp index 1096c10f01..998961c1e7 100644 --- a/netwerk/base/nsInputStreamPump.cpp +++ b/netwerk/base/nsInputStreamPump.cpp @@ -679,7 +679,7 @@ nsInputStreamPump::OnStateStop() MOZ_ASSERT(NS_IsMainThread(), "OnStateStop should only be called on the main thread."); nsresult rv = NS_DispatchToMainThread( - NS_NewRunnableMethod(this, &nsInputStreamPump::CallOnStateStop)); + NewRunnableMethod(this, &nsInputStreamPump::CallOnStateStop)); NS_ENSURE_SUCCESS(rv, STATE_IDLE); return STATE_IDLE; } diff --git a/netwerk/base/nsPACMan.cpp b/netwerk/base/nsPACMan.cpp index 169c3b51fa..c52854cf5b 100644 --- a/netwerk/base/nsPACMan.cpp +++ b/netwerk/base/nsPACMan.cpp @@ -415,10 +415,8 @@ nsPACMan::LoadPACFromURI(const nsCString &spec) // queries the enter between now and when we actually load the PAC file. if (!mLoadPending) { - nsCOMPtr event = - NS_NewRunnableMethod(this, &nsPACMan::StartLoading); nsresult rv; - if (NS_FAILED(rv = NS_DispatchToCurrentThread(event))) + if (NS_FAILED(rv = NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsPACMan::StartLoading)))) return rv; mLoadPending = true; } @@ -784,9 +782,9 @@ nsPACMan::Init(nsISystemProxySettings *systemProxySettings) if (NS_FAILED(rv)) return rv; - nsCOMPtr event = NS_NewRunnableMethod(this, &nsPACMan::NamePACThread); // don't check return value as it is not a big deal for this to fail. - mPACThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); + mPACThread->Dispatch(NewRunnableMethod(this, &nsPACMan::NamePACThread), + nsIEventTarget::DISPATCH_NORMAL); return NS_OK; } diff --git a/netwerk/base/nsServerSocket.cpp b/netwerk/base/nsServerSocket.cpp index 3822448263..a074bc7e0e 100644 --- a/netwerk/base/nsServerSocket.cpp +++ b/netwerk/base/nsServerSocket.cpp @@ -30,10 +30,7 @@ typedef void (nsServerSocket:: *nsServerSocketFunc)(void); static nsresult PostEvent(nsServerSocket *s, nsServerSocketFunc func) { - nsCOMPtr ev = NS_NewRunnableMethod(s, func); - if (!ev) - return NS_ERROR_OUT_OF_MEMORY; - + nsCOMPtr ev = NewRunnableMethod(s, func); if (!gSocketTransportService) return NS_ERROR_FAILURE; @@ -129,7 +126,7 @@ nsServerSocket::TryAttach() if (!gSocketTransportService->CanAttachSocket()) { nsCOMPtr event = - NS_NewRunnableMethod(this, &nsServerSocket::OnMsgAttach); + NewRunnableMethod(this, &nsServerSocket::OnMsgAttach); if (!event) return NS_ERROR_OUT_OF_MEMORY; diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index f7aaf2dc60..71634fbc7e 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -886,7 +886,7 @@ nsSocketTransportService::Run() mRawThread->HasPendingEvents(&pendingEvents); if (pendingEvents) { if (!mServingPendingQueue) { - nsresult rv = Dispatch(NS_NewRunnableMethod(this, + nsresult rv = Dispatch(NewRunnableMethod(this, &nsSocketTransportService::MarkTheLastElementOfPendingQueue), nsIEventTarget::DISPATCH_NORMAL); if (NS_FAILED(rv)) { @@ -1181,7 +1181,7 @@ nsSocketTransportService::OnKeepaliveEnabledPrefChange() // Dispatch to socket thread if we're not executing there. if (PR_GetCurrentThread() != gSocketThread) { gSocketTransportService->Dispatch( - NS_NewRunnableMethod( + NewRunnableMethod( this, &nsSocketTransportService::OnKeepaliveEnabledPrefChange), NS_DISPATCH_NORMAL); return; @@ -1233,8 +1233,8 @@ nsSocketTransportService::Observe(nsISupports *subject, if (!strcmp(topic, "last-pb-context-exited")) { nsCOMPtr ev = - NS_NewRunnableMethod(this, - &nsSocketTransportService::ClosePrivateConnections); + NewRunnableMethod(this, + &nsSocketTransportService::ClosePrivateConnections); nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/netwerk/base/nsUDPSocket.cpp b/netwerk/base/nsUDPSocket.cpp index b5b96a08a1..0e2afdd1e2 100644 --- a/netwerk/base/nsUDPSocket.cpp +++ b/netwerk/base/nsUDPSocket.cpp @@ -47,12 +47,10 @@ typedef void (nsUDPSocket:: *nsUDPSocketFunc)(void); static nsresult PostEvent(nsUDPSocket *s, nsUDPSocketFunc func) { - nsCOMPtr ev = NS_NewRunnableMethod(s, func); - if (!gSocketTransportService) return NS_ERROR_FAILURE; - return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL); + return gSocketTransportService->Dispatch(NewRunnableMethod(s, func), NS_DISPATCH_NORMAL); } static nsresult @@ -346,7 +344,7 @@ nsUDPSocket::TryAttach() if (!gSocketTransportService->CanAttachSocket()) { nsCOMPtr event = - NS_NewRunnableMethod(this, &nsUDPSocket::OnMsgAttach); + NewRunnableMethod(this, &nsUDPSocket::OnMsgAttach); nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); if (NS_FAILED(rv)) diff --git a/netwerk/cache/nsCacheService.cpp b/netwerk/cache/nsCacheService.cpp index c29457c8ef..081984ab91 100644 --- a/netwerk/cache/nsCacheService.cpp +++ b/netwerk/cache/nsCacheService.cpp @@ -1522,8 +1522,8 @@ nsresult nsCacheService::EvictEntriesInternal(nsCacheStoragePolicy storagePolicy if (storagePolicy == nsICache::STORE_ANYWHERE) { // if not called on main thread, dispatch the notification to the main thread to notify observers if (!NS_IsMainThread()) { - nsCOMPtr event = NS_NewRunnableMethod(this, - &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification); + nsCOMPtr event = NewRunnableMethod(this, + &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification); NS_DispatchToMainThread(event); } else { // else you're already on main thread - notify observers diff --git a/netwerk/cache2/CacheEntry.cpp b/netwerk/cache2/CacheEntry.cpp index 92179e200d..fda6111850 100644 --- a/netwerk/cache2/CacheEntry.cpp +++ b/netwerk/cache2/CacheEntry.cpp @@ -650,10 +650,9 @@ bool CacheEntry::InvokeCallbacks(bool aReadOnly) if (NS_SUCCEEDED(rv) && !onCheckThread) { // Redispatch to the target thread - RefPtr > event = - NS_NewRunnableMethod(this, &CacheEntry::InvokeCallbacksLock); - - rv = mCallbacks[i].mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); + rv = mCallbacks[i].mTargetThread->Dispatch(NewRunnableMethod(this, + &CacheEntry::InvokeCallbacksLock), + nsIEventTarget::DISPATCH_NORMAL); if (NS_SUCCEEDED(rv)) { LOG((" re-dispatching to target thread")); return false; @@ -1721,9 +1720,7 @@ void CacheEntry::BackgroundOp(uint32_t aOperations, bool aForceAsync) // Because CacheFile::Set*() are not thread-safe to use (uses WeakReference that // is not thread-safe) we must post to the main thread... - RefPtr > event = - NS_NewRunnableMethodWithArg(this, &CacheEntry::StoreFrecency, mFrecency); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(this, &CacheEntry::StoreFrecency, mFrecency)); } if (aOperations & Ops::REGISTER) { diff --git a/netwerk/cache2/CacheFileChunk.cpp b/netwerk/cache2/CacheFileChunk.cpp index 4b090a846d..51dff37052 100644 --- a/netwerk/cache2/CacheFileChunk.cpp +++ b/netwerk/cache2/CacheFileChunk.cpp @@ -54,9 +54,7 @@ CacheFileChunk::DispatchRelease() return false; } - RefPtr > event = - NS_NewNonOwningRunnableMethod(this, &CacheFileChunk::Release); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewNonOwningRunnableMethod(this, &CacheFileChunk::Release)); return true; } diff --git a/netwerk/cache2/CacheFileContextEvictor.cpp b/netwerk/cache2/CacheFileContextEvictor.cpp index b92dcacf7d..79966927c3 100644 --- a/netwerk/cache2/CacheFileContextEvictor.cpp +++ b/netwerk/cache2/CacheFileContextEvictor.cpp @@ -522,7 +522,7 @@ CacheFileContextEvictor::StartEvicting() } nsCOMPtr ev; - ev = NS_NewRunnableMethod(this, &CacheFileContextEvictor::EvictEntries); + ev = NewRunnableMethod(this, &CacheFileContextEvictor::EvictEntries); RefPtr ioThread = CacheFileIOManager::IOThread(); diff --git a/netwerk/cache2/CacheFileIOManager.cpp b/netwerk/cache2/CacheFileIOManager.cpp index ee3285962f..1ef087f8a7 100644 --- a/netwerk/cache2/CacheFileIOManager.cpp +++ b/netwerk/cache2/CacheFileIOManager.cpp @@ -67,9 +67,10 @@ CacheFileHandle::DispatchRelease() return false; } - RefPtr > event = - NS_NewNonOwningRunnableMethod(this, &CacheFileHandle::Release); - nsresult rv = ioTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); + nsresult rv = + ioTarget->Dispatch(NewNonOwningRunnableMethod(this, + &CacheFileHandle::Release), + nsIEventTarget::DISPATCH_NORMAL); if (NS_FAILED(rv)) { return false; } @@ -2635,8 +2636,8 @@ CacheFileIOManager::EvictIfOverLimit() } nsCOMPtr ev; - ev = NS_NewRunnableMethod(ioMan, - &CacheFileIOManager::EvictIfOverLimitInternal); + ev = NewRunnableMethod(ioMan, + &CacheFileIOManager::EvictIfOverLimitInternal); rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::EVICT); NS_ENSURE_SUCCESS(rv, rv); @@ -2696,8 +2697,8 @@ CacheFileIOManager::EvictIfOverLimitInternal() cacheUsage, cacheLimit)); nsCOMPtr ev; - ev = NS_NewRunnableMethod(this, - &CacheFileIOManager::OverLimitEvictionInternal); + ev = NewRunnableMethod(this, + &CacheFileIOManager::OverLimitEvictionInternal); rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT); NS_ENSURE_SUCCESS(rv, rv); @@ -2836,7 +2837,7 @@ CacheFileIOManager::EvictAll() } nsCOMPtr ev; - ev = NS_NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal); + ev = NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal); rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -2954,7 +2955,7 @@ CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo, bool aP } nsCOMPtr ev; - ev = NS_NewRunnableMethodWithArgs, bool> + ev = NewRunnableMethod, bool> (ioMan, &CacheFileIOManager::EvictByContextInternal, aLoadContextInfo, aPinned); rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev); @@ -3073,7 +3074,7 @@ CacheFileIOManager::CacheIndexStateChanged() // We have to re-distatch even if we are on IO thread to prevent reentering // the lock in CacheIndex nsCOMPtr ev; - ev = NS_NewRunnableMethod( + ev = NewRunnableMethod( gInstance, &CacheFileIOManager::CacheIndexStateChangedInternal); nsCOMPtr ioTarget = IOTarget(); @@ -3248,8 +3249,8 @@ CacheFileIOManager::StartRemovingTrash() } nsCOMPtr ev; - ev = NS_NewRunnableMethod(this, - &CacheFileIOManager::RemoveTrashInternal); + ev = NewRunnableMethod(this, + &CacheFileIOManager::RemoveTrashInternal); rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT); NS_ENSURE_SUCCESS(rv, rv); diff --git a/netwerk/cache2/CacheIndex.cpp b/netwerk/cache2/CacheIndex.cpp index 59de1c21fd..bda8538960 100644 --- a/netwerk/cache2/CacheIndex.cpp +++ b/netwerk/cache2/CacheIndex.cpp @@ -346,7 +346,7 @@ CacheIndex::PreShutdown() } nsCOMPtr event; - event = NS_NewRunnableMethod(index, &CacheIndex::PreShutdownInternal); + event = NewRunnableMethod(index, &CacheIndex::PreShutdownInternal); RefPtr ioThread = CacheFileIOManager::IOThread(); MOZ_ASSERT(ioThread); diff --git a/netwerk/cache2/CacheObserver.cpp b/netwerk/cache2/CacheObserver.cpp index f798328fae..5bbb4ade49 100644 --- a/netwerk/cache2/CacheObserver.cpp +++ b/netwerk/cache2/CacheObserver.cpp @@ -317,7 +317,7 @@ CacheObserver::SetDiskCacheCapacity(uint32_t aCapacity) sSelf->StoreDiskCacheCapacity(); } else { nsCOMPtr event = - NS_NewRunnableMethod(sSelf, &CacheObserver::StoreDiskCacheCapacity); + NewRunnableMethod(sSelf, &CacheObserver::StoreDiskCacheCapacity); NS_DispatchToMainThread(event); } } diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp index 2d0d5f5e2b..1d696a5e0c 100644 --- a/netwerk/cache2/CacheStorageService.cpp +++ b/netwerk/cache2/CacheStorageService.cpp @@ -143,7 +143,7 @@ void CacheStorageService::Shutdown() mShutdown = true; nsCOMPtr event = - NS_NewRunnableMethod(this, &CacheStorageService::ShutdownBackground); + NewRunnableMethod(this, &CacheStorageService::ShutdownBackground); Dispatch(event); mozilla::MutexAutoLock lock(mLock); @@ -1208,7 +1208,7 @@ CacheStorageService::OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer, // Dispatch as a priority task, we want to set the purge timer // ASAP to prevent vain redispatch of this event. nsCOMPtr event = - NS_NewRunnableMethod(this, &CacheStorageService::SchedulePurgeOverMemoryLimit); + NewRunnableMethod(this, &CacheStorageService::SchedulePurgeOverMemoryLimit); cacheIOTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); } @@ -1248,7 +1248,7 @@ CacheStorageService::Notify(nsITimer* aTimer) mPurgeTimer = nullptr; nsCOMPtr event = - NS_NewRunnableMethod(this, &CacheStorageService::PurgeOverMemoryLimit); + NewRunnableMethod(this, &CacheStorageService::PurgeOverMemoryLimit); Dispatch(event); } diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp index 44b27eb8fe..431fc8fbe6 100644 --- a/netwerk/dns/DNSRequestChild.cpp +++ b/netwerk/dns/DNSRequestChild.cpp @@ -207,7 +207,7 @@ DNSRequestChild::StartRequest() // we can only do IPDL on the main thread if (!NS_IsMainThread()) { NS_DispatchToMainThread( - NS_NewRunnableMethod(this, &DNSRequestChild::StartRequest)); + NewRunnableMethod(this, &DNSRequestChild::StartRequest)); return; } @@ -260,7 +260,7 @@ DNSRequestChild::RecvLookupCompleted(const DNSRequestResponse& reply) CallOnLookupComplete(); } else { nsCOMPtr event = - NS_NewRunnableMethod(this, &DNSRequestChild::CallOnLookupComplete); + NewRunnableMethod(this, &DNSRequestChild::CallOnLookupComplete); mTarget->Dispatch(event, NS_DISPATCH_NORMAL); } diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp index 5b205f925b..d66c3f571d 100644 --- a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp +++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp @@ -74,9 +74,8 @@ public: PR_Close(mFD); mFD = nullptr; - nsCOMPtr ev = - NS_NewRunnableMethod(this, &ServiceWatcher::Deallocate); - mThread->Dispatch(ev, NS_DISPATCH_NORMAL); + mThread->Dispatch(NewRunnableMethod(this, &ServiceWatcher::Deallocate), + NS_DISPATCH_NORMAL); } virtual void IsLocal(bool *aIsLocal) override { *aIsLocal = true; } @@ -154,8 +153,8 @@ private: nsresult PostEvent(void(ServiceWatcher::*func)(void)) { - nsCOMPtr ev = NS_NewRunnableMethod(this, func); - return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL); + return gSocketTransportService->Dispatch(NewRunnableMethod(this, func), + NS_DISPATCH_NORMAL); } void OnMsgClose() @@ -219,7 +218,7 @@ private: // if (!gSocketTransportService->CanAttachSocket()) { nsCOMPtr event = - NS_NewRunnableMethod(this, &ServiceWatcher::OnMsgAttach); + NewRunnableMethod(this, &ServiceWatcher::OnMsgAttach); nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); if (NS_FAILED(rv)) { diff --git a/netwerk/ipc/ChannelEventQueue.cpp b/netwerk/ipc/ChannelEventQueue.cpp index d0511e2d61..ae55ebd032 100644 --- a/netwerk/ipc/ChannelEventQueue.cpp +++ b/netwerk/ipc/ChannelEventQueue.cpp @@ -67,13 +67,13 @@ ChannelEventQueue::Resume() } if (!--mSuspendCount) { - RefPtr > event = - NS_NewRunnableMethod(this, &ChannelEventQueue::CompleteResume); + RefPtr event = + NewRunnableMethod(this, &ChannelEventQueue::CompleteResume); if (mTargetThread) { - mTargetThread->Dispatch(event, NS_DISPATCH_NORMAL); + mTargetThread->Dispatch(event.forget(), NS_DISPATCH_NORMAL); } else { MOZ_RELEASE_ASSERT(NS_IsMainThread()); - NS_WARN_IF(NS_FAILED(NS_DispatchToCurrentThread(event))); + NS_WARN_IF(NS_FAILED(NS_DispatchToCurrentThread(event.forget()))); } } } diff --git a/netwerk/protocol/about/nsAboutCache.cpp b/netwerk/protocol/about/nsAboutCache.cpp index 31f4a02c82..2ab2e719d5 100644 --- a/netwerk/protocol/about/nsAboutCache.cpp +++ b/netwerk/protocol/about/nsAboutCache.cpp @@ -179,9 +179,7 @@ nsAboutCache::VisitNextStorage() // from visitor callback. The cache v1 service doesn't like it. // TODO - mayhemer, bug 913828, remove this dispatch and call // directly. - nsCOMPtr event = - NS_NewRunnableMethod(this, &nsAboutCache::FireVisitStorage); - return NS_DispatchToMainThread(event); + return NS_DispatchToMainThread(mozilla::NewRunnableMethod(this, &nsAboutCache::FireVisitStorage)); } void diff --git a/netwerk/protocol/file/nsFileChannel.cpp b/netwerk/protocol/file/nsFileChannel.cpp index 8368161a01..38b59af4b6 100644 --- a/netwerk/protocol/file/nsFileChannel.cpp +++ b/netwerk/protocol/file/nsFileChannel.cpp @@ -232,7 +232,7 @@ nsFileUploadContentStream::AsyncWait(nsIInputStreamCallback *callback, if (IsNonBlocking()) { nsCOMPtr callback = - NS_NewRunnableMethod(this, &nsFileUploadContentStream::OnCopyComplete); + NewRunnableMethod(this, &nsFileUploadContentStream::OnCopyComplete); mCopyEvent->Dispatch(callback, mSink, target); } diff --git a/netwerk/protocol/ftp/FTPChannelParent.cpp b/netwerk/protocol/ftp/FTPChannelParent.cpp index cd2013c602..fbe3addde2 100644 --- a/netwerk/protocol/ftp/FTPChannelParent.cpp +++ b/netwerk/protocol/ftp/FTPChannelParent.cpp @@ -740,7 +740,7 @@ FTPChannelParent::DivertTo(nsIStreamListener *aListener) // Call OnStartRequest and SendDivertMessages asynchronously to avoid // reentering client context. NS_DispatchToCurrentThread( - NS_NewRunnableMethod(this, &FTPChannelParent::StartDiversion)); + NewRunnableMethod(this, &FTPChannelParent::StartDiversion)); return; } diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index ee9b1cfa7b..e1ac4f3beb 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -672,7 +672,7 @@ void CopyComplete(void* aClosure, nsresult aStatus) { // Called on the STS thread by NS_AsyncCopy auto channel = static_cast(aClosure); - nsCOMPtr runnable = NS_NewRunnableMethodWithArg( + nsCOMPtr runnable = NewRunnableMethod( channel, &HttpBaseChannel::EnsureUploadStreamIsCloneableComplete, aStatus); NS_DispatchToMainThread(runnable.forget()); } diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index 20083217cf..0bd97a9686 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -582,11 +582,11 @@ inline void HttpAsyncAborter::HandleAsyncAbort() template nsresult HttpAsyncAborter::AsyncCall(void (T::*funcPtr)(), - nsRunnableMethod **retval) + nsRunnableMethod **retval) { nsresult rv; - RefPtr > event = NS_NewRunnableMethod(mThis, funcPtr); + RefPtr> event = NewRunnableMethod(mThis, funcPtr); rv = NS_DispatchToCurrentThread(event); if (NS_SUCCEEDED(rv) && retval) { *retval = event; diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index c6bed6cd92..c10370b79f 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -1437,7 +1437,7 @@ HttpChannelParent::DivertTo(nsIStreamListener *aListener) // Call OnStartRequest and SendDivertMessages asynchronously to avoid // reentering client context. NS_DispatchToCurrentThread( - NS_NewRunnableMethod(this, &HttpChannelParent::StartDiversion)); + NewRunnableMethod(this, &HttpChannelParent::StartDiversion)); return; } diff --git a/netwerk/protocol/http/PackagedAppVerifier.cpp b/netwerk/protocol/http/PackagedAppVerifier.cpp index b520eab3fc..c0dac5f428 100644 --- a/netwerk/protocol/http/PackagedAppVerifier.cpp +++ b/netwerk/protocol/http/PackagedAppVerifier.cpp @@ -248,13 +248,13 @@ PackagedAppVerifier::FireVerifiedEvent(bool aForManifest, bool aSuccess) nsCOMPtr r; if (aForManifest) { - r = NS_NewRunnableMethodWithArgs(this, - &PackagedAppVerifier::OnManifestVerified, - aSuccess); + r = NewRunnableMethod(this, + &PackagedAppVerifier::OnManifestVerified, + aSuccess); } else { - r = NS_NewRunnableMethodWithArgs(this, - &PackagedAppVerifier::OnResourceVerified, - aSuccess); + r = NewRunnableMethod(this, + &PackagedAppVerifier::OnResourceVerified, + aSuccess); } NS_DispatchToMainThread(r); diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 5581c932c9..fb8e1cfef4 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -2108,8 +2108,8 @@ nsHttpHandler::Observe(nsISupports *subject, if (mConnMgr) { if (gSocketTransportService) { nsCOMPtr event = - NS_NewRunnableMethod(mConnMgr, - &nsHttpConnectionMgr::ClearConnectionHistory); + NewRunnableMethod(mConnMgr, + &nsHttpConnectionMgr::ClearConnectionHistory); gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); } mConnMgr->ClearAltServiceMappings(); diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index c9b0d74bea..2854479a9b 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -1248,7 +1248,7 @@ WebSocketChannel::Observe(nsISupports *subject, // Next we check mDataStarted, which we need to do on mTargetThread. if (!IsOnTargetThread()) { mTargetThread->Dispatch( - NS_NewRunnableMethod(this, &WebSocketChannel::OnNetworkChanged), + NewRunnableMethod(this, &WebSocketChannel::OnNetworkChanged), NS_DISPATCH_NORMAL); } else { OnNetworkChanged(); @@ -1272,7 +1272,7 @@ WebSocketChannel::OnNetworkChanged() } return mSocketThread->Dispatch( - NS_NewRunnableMethod(this, &WebSocketChannel::OnNetworkChanged), + NewRunnableMethod(this, &WebSocketChannel::OnNetworkChanged), NS_DISPATCH_NORMAL); } @@ -1359,7 +1359,7 @@ WebSocketChannel::BeginOpen(bool aCalledFromAdmissionManager) // When called from nsWSAdmissionManager post an event to avoid potential // re-entering of nsWSAdmissionManager and its lock. NS_DispatchToMainThread( - NS_NewRunnableMethod(this, &WebSocketChannel::BeginOpenInternal), + NewRunnableMethod(this, &WebSocketChannel::BeginOpenInternal), NS_DISPATCH_NORMAL); } else { BeginOpenInternal(); @@ -2836,7 +2836,7 @@ WebSocketChannel::StartWebsocketData() if (!IsOnTargetThread()) { return mTargetThread->Dispatch( - NS_NewRunnableMethod(this, &WebSocketChannel::StartWebsocketData), + NewRunnableMethod(this, &WebSocketChannel::StartWebsocketData), NS_DISPATCH_NORMAL); } @@ -2859,15 +2859,15 @@ WebSocketChannel::StartWebsocketData() LOG(("WebSocketChannel::StartWebsocketData mSocketIn->AsyncWait() failed " "with error 0x%08x", rv)); return mSocketThread->Dispatch( - NS_NewRunnableMethodWithArgs(this, - &WebSocketChannel::AbortSession, - rv), + NewRunnableMethod(this, + &WebSocketChannel::AbortSession, + rv), NS_DISPATCH_NORMAL); } if (mPingInterval) { rv = mSocketThread->Dispatch( - NS_NewRunnableMethod(this, &WebSocketChannel::StartPinging), + NewRunnableMethod(this, &WebSocketChannel::StartPinging), NS_DISPATCH_NORMAL); if (NS_FAILED(rv)) { LOG(("WebSocketChannel::StartWebsocketData Could not start pinging, " diff --git a/netwerk/protocol/websocket/WebSocketChannelChild.cpp b/netwerk/protocol/websocket/WebSocketChannelChild.cpp index d98167db75..b88a4698aa 100644 --- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp +++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp @@ -107,9 +107,9 @@ WebSocketChannelChild::MaybeReleaseIPCObject() } if (!NS_IsMainThread()) { - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &WebSocketChannelChild::MaybeReleaseIPCObject); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewRunnableMethod(this, + &WebSocketChannelChild::MaybeReleaseIPCObject))); return; } diff --git a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp index c73bf4287d..7f1a431317 100644 --- a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp +++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp @@ -700,7 +700,7 @@ nsWyciwygChannel::OnCacheEntryAvailable(nsICacheEntry *aCacheEntry, if (!aNew) { // Since OnCacheEntryAvailable can be called directly from AsyncOpen // we must dispatch. - NS_DispatchToCurrentThread(NS_NewRunnableMethod( + NS_DispatchToCurrentThread(mozilla::NewRunnableMethod( this, &nsWyciwygChannel::NotifyListener)); } } diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index d3d8c8e34a..3a260e6ebb 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -233,11 +233,8 @@ nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason) // works out so that we get to terminate and clean up the parser from // a safer point. if (mParser) { // can mParser ever be null here? - nsCOMPtr terminator = - NS_NewRunnableMethod(GetParser(), &nsHtml5Parser::Terminate); - if (NS_FAILED(NS_DispatchToMainThread(terminator))) { - NS_WARNING("failed to dispatch executor flush event"); - } + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewRunnableMethod(GetParser(), &nsHtml5Parser::Terminate))); } return aReason; } diff --git a/security/apps/AppSignatureVerification.cpp b/security/apps/AppSignatureVerification.cpp index fdfd18d424..38dbb69414 100644 --- a/security/apps/AppSignatureVerification.cpp +++ b/security/apps/AppSignatureVerification.cpp @@ -12,6 +12,7 @@ #include "ScopedNSSTypes.h" #include "base64.h" #include "certdb.h" +#include "mozilla/Casting.h" #include "mozilla/Logging.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" @@ -622,7 +623,7 @@ ParseMF(const char* filebuf, nsIZipReader * zip, struct VerifyCertificateContext { AppTrustedRoot trustedRoot; - ScopedCERTCertList& builtChain; + UniqueCERTCertList& builtChain; }; nsresult @@ -633,7 +634,7 @@ VerifyCertificate(CERTCertificate* signerCert, void* voidContext, void* pinArg) return NS_ERROR_INVALID_ARG; } const VerifyCertificateContext& context = - *reinterpret_cast(voidContext); + *static_cast(voidContext); AppTrustDomain trustDomain(context.builtChain, pinArg); if (trustDomain.SetTrustedRoot(context.trustedRoot) != SECSuccess) { @@ -682,7 +683,7 @@ VerifyCertificate(CERTCertificate* signerCert, void* voidContext, void* pinArg) nsresult VerifySignature(AppTrustedRoot trustedRoot, const SECItem& buffer, const SECItem& detachedDigest, - /*out*/ ScopedCERTCertList& builtChain) + /*out*/ UniqueCERTCertList& builtChain) { // Currently, this function is only called within the CalculateResult() method // of CryptoTasks. As such, NSS should not be shut down at this point and the @@ -742,7 +743,7 @@ OpenSignedAppFile(AppTrustedRoot aTrustedRoot, nsIFile* aJarFile, } sigBuffer.type = siBuffer; - ScopedCERTCertList builtChain; + UniqueCERTCertList builtChain; rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(), builtChain); if (NS_FAILED(rv)) { @@ -916,14 +917,14 @@ VerifySignedManifest(AppTrustedRoot aTrustedRoot, // Calculate SHA1 digest of the base64 encoded string Digest doubleDigest; rv = doubleDigest.DigestBuf(SEC_OID_SHA1, - reinterpret_cast(base64EncDigest.get()), + BitwiseCast(base64EncDigest.get()), strlen(base64EncDigest.get())); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // Verify the manifest signature (signed digest of the base64 encoded string) - ScopedCERTCertList builtChain; + UniqueCERTCertList builtChain; rv = VerifySignature(aTrustedRoot, signatureBuffer, doubleDigest.get(), builtChain); if (NS_FAILED(rv)) { @@ -1422,7 +1423,7 @@ VerifySignedDirectory(AppTrustedRoot aTrustedRoot, } sigBuffer.type = siBuffer; - ScopedCERTCertList builtChain; + UniqueCERTCertList builtChain; rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(), builtChain); if (NS_FAILED(rv)) { diff --git a/security/apps/AppTrustDomain.cpp b/security/apps/AppTrustDomain.cpp index 4f6bc68ff2..bc4e7689a0 100644 --- a/security/apps/AppTrustDomain.cpp +++ b/security/apps/AppTrustDomain.cpp @@ -5,17 +5,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AppTrustDomain.h" -#include "certdb.h" -#include "pkix/pkixnss.h" -#include "mozilla/ArrayUtils.h" #include "MainThreadUtils.h" +#include "certdb.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Casting.h" #include "mozilla/Preferences.h" #include "nsComponentManagerUtils.h" #include "nsIFile.h" #include "nsIFileStreams.h" #include "nsIX509CertDB.h" -#include "nsNetUtil.h" #include "nsNSSCertificate.h" +#include "nsNetUtil.h" +#include "pkix/pkixnss.h" #include "prerror.h" #include "secerr.h" @@ -49,7 +50,7 @@ StaticMutex AppTrustDomain::sMutex; UniquePtr AppTrustDomain::sDevImportedDERData; unsigned int AppTrustDomain::sDevImportedDERLen = 0; -AppTrustDomain::AppTrustDomain(ScopedCERTCertList& certChain, void* pinArg) +AppTrustDomain::AppTrustDomain(UniqueCERTCertList& certChain, void* pinArg) : mCertChain(certChain) , mPinArg(pinArg) , mMinRSABits(DEFAULT_MIN_RSA_BITS) @@ -152,7 +153,8 @@ AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot) } MOZ_ASSERT(length == sDevImportedDERLen); - sDevImportedDERData.reset(reinterpret_cast(data.release())); + sDevImportedDERData.reset( + BitwiseCast(data.release())); } trustedDER.data = sDevImportedDERData.get(); @@ -194,7 +196,7 @@ AppTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker, // message, passing each one to checker.Check. SECItem encodedIssuerNameSECItem = UnsafeMapInputToSECItem(encodedIssuerName); - ScopedCERTCertList + UniqueCERTCertList candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameSECItem, 0, false)); diff --git a/security/apps/AppTrustDomain.h b/security/apps/AppTrustDomain.h index 29a9ac20bb..6410a78ee7 100644 --- a/security/apps/AppTrustDomain.h +++ b/security/apps/AppTrustDomain.h @@ -4,8 +4,8 @@ * 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 mozilla_psm_AppsTrustDomain_h -#define mozilla_psm_AppsTrustDomain_h +#ifndef AppTrustDomain_h +#define AppTrustDomain_h #include "pkix/pkixtypes.h" #include "mozilla/StaticMutex.h" @@ -21,7 +21,7 @@ class AppTrustDomain final : public mozilla::pkix::TrustDomain public: typedef mozilla::pkix::Result Result; - AppTrustDomain(ScopedCERTCertList&, void* pinArg); + AppTrustDomain(UniqueCERTCertList& certChain, void* pinArg); SECStatus SetTrustedRoot(AppTrustedRoot trustedRoot); @@ -67,7 +67,7 @@ public: size_t digestBufLen) override; private: - /*out*/ ScopedCERTCertList& mCertChain; + /*out*/ UniqueCERTCertList& mCertChain; void* mPinArg; // non-owning! UniqueCERTCertificate mTrustedRoot; unsigned int mMinRSABits; @@ -79,4 +79,4 @@ private: } } // namespace mozilla::psm -#endif // mozilla_psm_AppsTrustDomain_h +#endif // AppTrustDomain_h diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp index 0f229bc598..a34232a418 100644 --- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -61,7 +61,7 @@ InitCertVerifierLog() } Result -IsCertChainRootBuiltInRoot(CERTCertList* chain, bool& result) +IsCertChainRootBuiltInRoot(const UniqueCERTCertList& chain, bool& result) { if (!chain || CERT_LIST_EMPTY(chain)) { return Result::FATAL_ERROR_LIBRARY_FAILURE; @@ -181,7 +181,7 @@ static const unsigned int MIN_RSA_BITS_WEAK = 1024; SECStatus CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, Time time, void* pinArg, const char* hostname, - /*out*/ ScopedCERTCertList& builtChain, + /*out*/ UniqueCERTCertList& builtChain, /*optional*/ const Flags flags, /*optional*/ const SECItem* stapledOCSPResponseSECItem, /*optional out*/ SECOidTag* evOidPolicy, @@ -650,7 +650,7 @@ CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert, Time time, /*optional*/ void* pinarg, const char* hostname, - /*out*/ ScopedCERTCertList& builtChain, + /*out*/ UniqueCERTCertList& builtChain, /*optional*/ bool saveIntermediatesInPermanentDatabase, /*optional*/ Flags flags, /*optional out*/ SECOidTag* evOidPolicy, diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h index 7a73d28e2e..fabc11bf51 100644 --- a/security/certverifier/CertVerifier.h +++ b/security/certverifier/CertVerifier.h @@ -4,8 +4,8 @@ * 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 mozilla_psm__CertVerifier_h -#define mozilla_psm__CertVerifier_h +#ifndef CertVerifier_h +#define CertVerifier_h #include "BRNameMatchingPolicy.h" #include "OCSPCache.h" @@ -74,7 +74,7 @@ public: mozilla::pkix::Time time, void* pinArg, const char* hostname, - /*out*/ ScopedCERTCertList& builtChain, + /*out*/ UniqueCERTCertList& builtChain, Flags flags = 0, /*optional in*/ const SECItem* stapledOCSPResponse = nullptr, /*optional out*/ SECOidTag* evOidPolicy = nullptr, @@ -89,7 +89,7 @@ public: mozilla::pkix::Time time, /*optional*/ void* pinarg, const char* hostname, - /*out*/ ScopedCERTCertList& builtChain, + /*out*/ UniqueCERTCertList& builtChain, /*optional*/ bool saveIntermediatesInPermanentDatabase = false, /*optional*/ Flags flags = 0, /*optional out*/ SECOidTag* evOidPolicy = nullptr, @@ -154,4 +154,4 @@ mozilla::pkix::Result CertListContainsExpectedKeys( } } // namespace mozilla::psm -#endif // mozilla_psm__CertVerifier_h +#endif // CertVerifier_h diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index e99df42989..0674948881 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -9,25 +9,26 @@ #include #include "ExtendedValidation.h" +#include "NSSErrorsService.h" #include "OCSPRequestor.h" #include "OCSPVerificationTrustDomain.h" -#include "certdb.h" +#include "PublicKeyPinningService.h" #include "cert.h" +#include "certdb.h" +#include "mozilla/Casting.h" #include "mozilla/UniquePtr.h" +#include "mozilla/unused.h" #include "nsNSSCertificate.h" #include "nsThreadUtils.h" -#include "nss.h" -#include "NSSErrorsService.h" #include "nsServiceManagerUtils.h" +#include "nss.h" #include "pk11pub.h" +#include "pkix/Result.h" #include "pkix/pkix.h" #include "pkix/pkixnss.h" -#include "pkix/Result.h" #include "prerror.h" #include "prmem.h" #include "prprf.h" -#include "PublicKeyPinningService.h" -#include "ScopedNSSTypes.h" #include "secerr.h" #include "CNNICHashWhitelist.inc" @@ -53,7 +54,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType, unsigned int minRSABits, ValidityCheckingMode validityCheckingMode, CertVerifier::SHA1Mode sha1Mode, - ScopedCERTCertList& builtChain, + UniqueCERTCertList& builtChain, /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo, /*optional*/ const char* hostname) : mCertDBTrustType(certDBTrustType) @@ -77,7 +78,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType, // If useRoots is true, we only use root certificates in the candidate list. // If useRoots is false, we only use non-root certificates in the list. static Result -FindIssuerInner(ScopedCERTCertList& candidates, bool useRoots, +FindIssuerInner(const UniqueCERTCertList& candidates, bool useRoots, Input encodedIssuerName, TrustDomain::IssuerChecker& checker, /*out*/ bool& keepGoing) { @@ -137,7 +138,7 @@ NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName, // TODO: NSS seems to be ambiguous between "no potential issuers found" and // "there was an error trying to retrieve the potential issuers." SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName); - ScopedCERTCertList + UniqueCERTCertList candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameItem, 0, false)); @@ -736,7 +737,7 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time) MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("NSSCertDBTrustDomain: IsChainValid")); - ScopedCERTCertList certList; + UniqueCERTCertList certList; SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray, certList); if (srv != SECSuccess) { @@ -777,7 +778,7 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time) return Result::FATAL_ERROR_LIBRARY_FAILURE; } const uint8_t* certHash( - reinterpret_cast(digest.get().data)); + BitwiseCast(digest.get().data)); size_t certHashLen = digest.get().len; size_t unused; if (!mozilla::BinarySearchIf(WhitelistedCNNICHashes, 0, @@ -814,7 +815,7 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time) } } - mBuiltChain = certList.forget(); + mBuiltChain = Move(certList); return Success; } @@ -1023,16 +1024,16 @@ LoadLoadableRoots(/*optional*/ const char* dir, const char* modNameUTF8) int modType; SECMOD_DeleteModule(modNameUTF8, &modType); - UniquePtr - pkcs11ModuleSpec(PR_smprintf("name=\"%s\" library=\"%s\"", modNameUTF8, - escapedFullLibraryPath.get()), - PR_smprintf_free); - if (!pkcs11ModuleSpec) { + nsAutoCString pkcs11ModuleSpec; + pkcs11ModuleSpec.AppendPrintf("name=\"%s\" library=\"%s\"", modNameUTF8, + escapedFullLibraryPath.get()); + if (pkcs11ModuleSpec.IsEmpty()) { return SECFailure; } - UniqueSECMODModule rootsModule(SECMOD_LoadUserModule(pkcs11ModuleSpec.get(), - nullptr, false)); + UniqueSECMODModule rootsModule( + SECMOD_LoadUserModule(const_cast(pkcs11ModuleSpec.get()), nullptr, + false)); if (!rootsModule) { return SECFailure; } @@ -1056,69 +1057,69 @@ UnloadLoadableRoots(const char* modNameUTF8) } } -char* -DefaultServerNicknameForCert(CERTCertificate* cert) +nsresult +DefaultServerNicknameForCert(const CERTCertificate* cert, + /*out*/ nsCString& nickname) { - char* nickname = nullptr; - int count; - bool conflict; - char* servername = nullptr; + MOZ_ASSERT(cert); + NS_ENSURE_ARG_POINTER(cert); - servername = CERT_GetCommonName(&cert->subject); - if (!servername) { - // Certs without common names are strange, but they do exist... - // Let's try to use another string for the nickname - servername = CERT_GetOrgUnitName(&cert->subject); - if (!servername) { - servername = CERT_GetOrgName(&cert->subject); - if (!servername) { - servername = CERT_GetLocalityName(&cert->subject); - if (!servername) { - servername = CERT_GetStateName(&cert->subject); - if (!servername) { - servername = CERT_GetCountryName(&cert->subject); - if (!servername) { - // We tried hard, there is nothing more we can do. - // A cert without any names doesn't really make sense. - return nullptr; - } - } - } - } - } + UniquePORTString baseName(CERT_GetCommonName(&cert->subject)); + if (!baseName) { + baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject)); + } + if (!baseName) { + baseName = UniquePORTString(CERT_GetOrgName(&cert->subject)); + } + if (!baseName) { + baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject)); + } + if (!baseName) { + baseName = UniquePORTString(CERT_GetStateName(&cert->subject)); + } + if (!baseName) { + baseName = UniquePORTString(CERT_GetCountryName(&cert->subject)); + } + if (!baseName) { + return NS_ERROR_FAILURE; } - count = 1; - while (1) { - if (count == 1) { - nickname = PR_smprintf("%s", servername); + // This function is only used in contexts where a failure to find a suitable + // nickname does not block the overall task from succeeding. + // As such, we use an arbitrary limit to prevent this nickname searching + // process from taking forever. + static const uint32_t ARBITRARY_LIMIT = 500; + for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) { + nickname = baseName.get(); + if (count != 1) { + nickname.AppendPrintf(" #%u", count); } - else { - nickname = PR_smprintf("%s #%d", servername, count); - } - if (!nickname) { - break; + if (nickname.IsEmpty()) { + return NS_ERROR_FAILURE; } - conflict = SEC_CertNicknameConflict(nickname, &cert->derSubject, - cert->dbhandle); + bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject, + cert->dbhandle); if (!conflict) { - break; + return NS_OK; } - PR_Free(nickname); - count++; } - PR_FREEIF(servername); - return nickname; + + return NS_ERROR_FAILURE; } void -SaveIntermediateCerts(const ScopedCERTCertList& certList) +SaveIntermediateCerts(const UniqueCERTCertList& certList) { if (!certList) { return; } + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); + if (!slot) { + return; + } + bool isEndEntity = true; for (CERTCertListNode* node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); @@ -1140,15 +1141,19 @@ SaveIntermediateCerts(const ScopedCERTCertList& certList) } // We have found a signer cert that we want to remember. - char* nickname = DefaultServerNicknameForCert(node->cert); - if (nickname && *nickname) { - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); - if (slot) { - PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE, - nickname, false); - } + nsAutoCString nickname; + nsresult rv = DefaultServerNicknameForCert(node->cert, nickname); + if (NS_FAILED(rv)) { + continue; } - PR_FREEIF(nickname); + + // Saving valid intermediate certs to the database is a compatibility hack + // to work around unknown issuer errors for incorrectly configured servers + // that fail to send the necessary intermediate certs. As such, we ignore + // the return value of PK11_ImportCert(), since it doesn't really matter if + // it fails. + Unused << PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE, + nickname.get(), false); } } diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h index 298f6de78e..f34f22229a 100644 --- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -4,11 +4,13 @@ * 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 mozilla_psm__NSSCertDBTrustDomain_h -#define mozilla_psm__NSSCertDBTrustDomain_h +#ifndef NSSCertDBTrustDomain_h +#define NSSCertDBTrustDomain_h #include "CertVerifier.h" +#include "ScopedNSSTypes.h" #include "nsICertBlocklist.h" +#include "nsString.h" #include "pkix/pkixtypes.h" #include "secmodt.h" @@ -36,10 +38,10 @@ SECStatus LoadLoadableRoots(/*optional*/ const char* dir, void UnloadLoadableRoots(const char* modNameUTF8); -// Caller must free the result with PR_Free -char* DefaultServerNicknameForCert(CERTCertificate* cert); +nsresult DefaultServerNicknameForCert(const CERTCertificate* cert, + /*out*/ nsCString& nickname); -void SaveIntermediateCerts(const ScopedCERTCertList& certList); +void SaveIntermediateCerts(const UniqueCERTCertList& certList); class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { @@ -63,7 +65,7 @@ public: unsigned int minRSABits, ValidityCheckingMode validityCheckingMode, CertVerifier::SHA1Mode sha1Mode, - ScopedCERTCertList& builtChain, + UniqueCERTCertList& builtChain, /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, /*optional*/ const char* hostname = nullptr); @@ -149,7 +151,7 @@ private: const unsigned int mMinRSABits; ValidityCheckingMode mValidityCheckingMode; CertVerifier::SHA1Mode mSHA1Mode; - ScopedCERTCertList& mBuiltChain; // non-owning + UniqueCERTCertList& mBuiltChain; // non-owning PinningTelemetryInfo* mPinningTelemetryInfo; const char* mHostname; // non-owning - only used for pinning checks nsCOMPtr mCertBlocklist; @@ -158,4 +160,4 @@ private: } } // namespace mozilla::psm -#endif // mozilla_psm__NSSCertDBTrustDomain_h +#endif // NSSCertDBTrustDomain_h diff --git a/security/certverifier/OCSPRequestor.cpp b/security/certverifier/OCSPRequestor.cpp index b19b476f39..36cf6db3fe 100644 --- a/security/certverifier/OCSPRequestor.cpp +++ b/security/certverifier/OCSPRequestor.cpp @@ -10,6 +10,7 @@ #include "ScopedNSSTypes.h" #include "mozilla/Base64.h" +#include "mozilla/Casting.h" #include "nsIURLParser.h" #include "nsNSSCallbacks.h" #include "nsNetCID.h" @@ -49,7 +50,8 @@ AppendEscapedBase64Item(const SECItem* encodedRequest, nsACString& path) { nsresult rv; nsDependentCSubstring requestAsSubstring( - reinterpret_cast(encodedRequest->data), encodedRequest->len); + BitwiseCast(encodedRequest->data), + encodedRequest->len); nsCString base64Request; rv = Base64Encode(requestAsSubstring, base64Request); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -140,14 +142,13 @@ DoOCSPRequest(const UniquePLArenaPool& arena, const char* url, hostname(url + authorityPos + hostnamePos, static_cast(hostnameLen)); - SEC_HTTP_SERVER_SESSION serverSessionPtr = nullptr; + nsNSSHttpServerSession* serverSessionPtr = nullptr; Result rv = nsNSSHttpInterface::createSessionFcn( hostname.BeginReading(), static_cast(port), &serverSessionPtr); if (rv != Success) { return rv; } - UniqueHTTPServerSession serverSession( - reinterpret_cast(serverSessionPtr)); + UniqueHTTPServerSession serverSession(serverSessionPtr); nsAutoCString path; if (pathLen > 0) { @@ -170,19 +171,19 @@ DoOCSPRequest(const UniquePLArenaPool& arena, const char* url, } } - SEC_HTTP_REQUEST_SESSION requestSessionPtr; + nsNSSHttpRequestSession* requestSessionPtr; rv = nsNSSHttpInterface::createFcn(serverSession.get(), "http", path.get(), method.get(), timeout, &requestSessionPtr); if (rv != Success) { return rv; } - UniqueHTTPRequestSession requestSession( - reinterpret_cast(requestSessionPtr)); + UniqueHTTPRequestSession requestSession(requestSessionPtr); if (!useGET) { rv = nsNSSHttpInterface::setPostDataFcn( - requestSession.get(), reinterpret_cast(encodedRequest->data), + requestSession.get(), + BitwiseCast(encodedRequest->data), encodedRequest->len, "application/ocsp-request"); if (rv != Success) { return rv; diff --git a/security/manager/ssl/CSTrustDomain.cpp b/security/manager/ssl/CSTrustDomain.cpp index 3c18340f7b..e2f334a776 100644 --- a/security/manager/ssl/CSTrustDomain.cpp +++ b/security/manager/ssl/CSTrustDomain.cpp @@ -20,7 +20,7 @@ namespace mozilla { namespace psm { static LazyLogModule gTrustDomainPRLog("CSTrustDomain"); #define CSTrust_LOG(args) MOZ_LOG(gTrustDomainPRLog, LogLevel::Debug, args) -CSTrustDomain::CSTrustDomain(ScopedCERTCertList& certChain) +CSTrustDomain::CSTrustDomain(UniqueCERTCertList& certChain) : mCertChain(certChain) , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)) { diff --git a/security/manager/ssl/CSTrustDomain.h b/security/manager/ssl/CSTrustDomain.h index 66f846a6d0..2627b17210 100644 --- a/security/manager/ssl/CSTrustDomain.h +++ b/security/manager/ssl/CSTrustDomain.h @@ -22,7 +22,7 @@ class CSTrustDomain final : public mozilla::pkix::TrustDomain public: typedef mozilla::pkix::Result Result; - explicit CSTrustDomain(ScopedCERTCertList& certChain); + explicit CSTrustDomain(UniqueCERTCertList& certChain); virtual Result GetCertTrust( mozilla::pkix::EndEntityOrCA endEntityOrCA, @@ -66,7 +66,7 @@ public: size_t digestBufLen) override; private: - /*out*/ ScopedCERTCertList& mCertChain; + /*out*/ UniqueCERTCertList& mCertChain; nsCOMPtr mCertBlocklist; }; diff --git a/security/manager/ssl/CertBlocklist.cpp b/security/manager/ssl/CertBlocklist.cpp index 163fe7955f..066497127b 100644 --- a/security/manager/ssl/CertBlocklist.cpp +++ b/security/manager/ssl/CertBlocklist.cpp @@ -5,7 +5,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CertBlocklist.h" + #include "mozilla/Base64.h" +#include "mozilla/Casting.h" +#include "mozilla/Logging.h" #include "mozilla/Preferences.h" #include "mozilla/unused.h" #include "nsAppDirectoryServiceDefs.h" @@ -21,7 +24,6 @@ #include "nsTHashtable.h" #include "nsThreadUtils.h" #include "pkix/Input.h" -#include "mozilla/Logging.h" #include "prtime.h" NS_IMPL_ISUPPORTS(CertBlocklist, nsICertBlocklist) @@ -82,9 +84,9 @@ CertBlocklistItem::~CertBlocklistItem() nsresult CertBlocklistItem::ToBase64(nsACString& b64DNOut, nsACString& b64OtherOut) { - nsDependentCSubstring DNString(reinterpret_cast(mDNData), + nsDependentCSubstring DNString(BitwiseCast(mDNData), mDNLength); - nsDependentCSubstring otherString(reinterpret_cast(mOtherData), + nsDependentCSubstring otherString(BitwiseCast(mOtherData), mOtherLength); nsresult rv = Base64Encode(DNString, b64DNOut); if (NS_FAILED(rv)) { @@ -344,25 +346,24 @@ CertBlocklist::AddRevokedCertInternal(const nsACString& aEncodedDN, CertBlocklistItemState aItemState, MutexAutoLock& /*proofOfLock*/) { - nsCString decodedDN; - nsCString decodedOther; - - nsresult rv = Base64Decode(aEncodedDN, decodedDN); - if (NS_FAILED(rv)) { - return rv; - } - rv = Base64Decode(aEncodedOther, decodedOther); - if (NS_FAILED(rv)) { - return rv; - } - - CertBlocklistItem item(reinterpret_cast(decodedDN.get()), - decodedDN.Length(), - reinterpret_cast(decodedOther.get()), - decodedOther.Length(), - aMechanism); + nsCString decodedDN; + nsCString decodedOther; + nsresult rv = Base64Decode(aEncodedDN, decodedDN); + if (NS_FAILED(rv)) { + return rv; + } + rv = Base64Decode(aEncodedOther, decodedOther); + if (NS_FAILED(rv)) { + return rv; + } + CertBlocklistItem item( + BitwiseCast(decodedDN.get()), + decodedDN.Length(), + BitwiseCast(decodedOther.get()), + decodedOther.Length(), + aMechanism); if (aItemState == CertNewFromBlocklist) { // We want SaveEntries to be a no-op if no new entries are added. @@ -584,8 +585,7 @@ CertBlocklist::IsCertRevoked(const uint8_t* aIssuer, return rv; } - rv = crypto->Update(reinterpret_cast(aPubKey), - aPubKeyLength); + rv = crypto->Update(aPubKey, aPubKeyLength); if (NS_FAILED(rv)) { return rv; } @@ -596,11 +596,12 @@ CertBlocklist::IsCertRevoked(const uint8_t* aIssuer, return rv; } - CertBlocklistItem subjectPubKey(aSubject, - static_cast(aSubjectLength), - reinterpret_cast(hashString.get()), - hashString.Length(), - BlockBySubjectAndPubKey); + CertBlocklistItem subjectPubKey( + aSubject, + static_cast(aSubjectLength), + BitwiseCast(hashString.get()), + hashString.Length(), + BlockBySubjectAndPubKey); rv = subjectPubKey.ToBase64(encDN, encOther); if (NS_FAILED(rv)) { @@ -649,7 +650,7 @@ void CertBlocklist::PreferenceChanged(const char* aPref, void* aClosure) { - CertBlocklist* blocklist = reinterpret_cast(aClosure); + auto blocklist = static_cast(aClosure); MutexAutoLock lock(blocklist->mMutex); MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, diff --git a/security/manager/ssl/ContentSignatureVerifier.cpp b/security/manager/ssl/ContentSignatureVerifier.cpp index 3cc6586211..a693f72ce7 100644 --- a/security/manager/ssl/ContentSignatureVerifier.cpp +++ b/security/manager/ssl/ContentSignatureVerifier.cpp @@ -7,18 +7,19 @@ #include "ContentSignatureVerifier.h" #include "BRNameMatchingPolicy.h" +#include "SharedCertVerifier.h" #include "cryptohi.h" #include "keyhi.h" +#include "mozilla/Casting.h" #include "nsCOMPtr.h" #include "nsNSSComponent.h" -#include "nssb64.h" +#include "nsSecurityHeaderParser.h" #include "nsWhitespaceTokenizer.h" #include "nsXPCOMStrings.h" +#include "nssb64.h" #include "pkix/pkix.h" #include "pkix/pkixtypes.h" #include "secerr.h" -#include "SharedCertVerifier.h" -#include "nsSecurityHeaderParser.h" NS_IMPL_ISUPPORTS(ContentSignatureVerifier, nsIContentSignatureVerifier) @@ -146,8 +147,7 @@ ContentSignatureVerifier::CreateContext(const nsACString& aData, return NS_ERROR_ALREADY_INITIALIZED; } - ScopedCERTCertList certCertList(CERT_NewCertList()); - + UniqueCERTCertList certCertList(CERT_NewCertList()); if (!certCertList) { return NS_ERROR_OUT_OF_MEMORY; } @@ -166,7 +166,7 @@ ContentSignatureVerifier::CreateContext(const nsACString& aData, Input certDER; Result result = - certDER.Init(reinterpret_cast(certSecItem->data), + certDER.Init(BitwiseCast(certSecItem->data), certSecItem->len); if (result != Success) { return NS_ERROR_FAILURE; diff --git a/security/manager/ssl/DataStorage.cpp b/security/manager/ssl/DataStorage.cpp index 72c9717a85..2059bb5f8d 100644 --- a/security/manager/ssl/DataStorage.cpp +++ b/security/manager/ssl/DataStorage.cpp @@ -193,9 +193,9 @@ DataStorage::Reader::~Reader() // This is for tests. nsCOMPtr job = - NS_NewRunnableMethodWithArg(mDataStorage, - &DataStorage::NotifyObservers, - "data-storage-ready"); + NewRunnableMethod(mDataStorage, + &DataStorage::NotifyObservers, + "data-storage-ready"); nsresult rv = NS_DispatchToMainThread(job, NS_DISPATCH_NORMAL); Unused << NS_WARN_IF(NS_FAILED(rv)); } @@ -686,9 +686,9 @@ DataStorage::Writer::Run() // Observed by tests. nsCOMPtr job = - NS_NewRunnableMethodWithArg(mDataStorage, - &DataStorage::NotifyObservers, - "data-storage-written"); + NewRunnableMethod(mDataStorage, + &DataStorage::NotifyObservers, + "data-storage-written"); rv = NS_DispatchToMainThread(job, NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -777,7 +777,7 @@ DataStorage::AsyncSetTimer(const MutexAutoLock& /*aProofOfLock*/) mPendingWrite = true; nsCOMPtr job = - NS_NewRunnableMethod(this, &DataStorage::SetTimer); + NewRunnableMethod(this, &DataStorage::SetTimer); nsresult rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -827,7 +827,7 @@ DataStorage::DispatchShutdownTimer(const MutexAutoLock& /*aProofOfLock*/) MOZ_ASSERT(XRE_IsParentProcess()); nsCOMPtr job = - NS_NewRunnableMethod(this, &DataStorage::ShutdownTimer); + NewRunnableMethod(this, &DataStorage::ShutdownTimer); nsresult rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/security/manager/ssl/LocalCertService.cpp b/security/manager/ssl/LocalCertService.cpp index 578009780b..7908b0374f 100644 --- a/security/manager/ssl/LocalCertService.cpp +++ b/security/manager/ssl/LocalCertService.cpp @@ -4,10 +4,12 @@ #include "LocalCertService.h" +#include "CryptoTask.h" +#include "ScopedNSSTypes.h" +#include "cert.h" +#include "mozilla/Casting.h" #include "mozilla/ModuleUtils.h" #include "mozilla/RefPtr.h" -#include "cert.h" -#include "CryptoTask.h" #include "nsIPK11Token.h" #include "nsIPK11TokenDB.h" #include "nsIX509Cert.h" @@ -18,7 +20,6 @@ #include "nsServiceManagerUtils.h" #include "nsString.h" #include "pk11pub.h" -#include "ScopedNSSTypes.h" namespace mozilla { @@ -109,7 +110,7 @@ private: nsresult rv; // Get the key slot for generation later - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } @@ -141,12 +142,12 @@ private: memcpy(keyParams.data + 2, curveOidData->oid.data, curveOidData->oid.len); // Generate cert key pair - ScopedSECKEYPrivateKey privateKey; ScopedSECKEYPublicKey publicKey; SECKEYPublicKey* tempPublicKey; - privateKey = PK11_GenerateKeyPair(slot, CKM_EC_KEY_PAIR_GEN, &keyParams, - &tempPublicKey, true /* token */, - true /* sensitive */, nullptr); + UniqueSECKEYPrivateKey privateKey( + PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &keyParams, + &tempPublicKey, true /* token */, + true /* sensitive */, nullptr)); if (!privateKey) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } @@ -181,10 +182,9 @@ private: // Generate random serial unsigned long serial; // This serial in principle could collide, but it's unlikely - rv = MapSECStatus( - PK11_GenerateRandomOnSlot(slot, - reinterpret_cast(&serial), - sizeof(serial))); + rv = MapSECStatus(PK11_GenerateRandomOnSlot( + slot.get(), BitwiseCast(&serial), + sizeof(serial))); if (NS_FAILED(rv)) { return rv; } @@ -224,7 +224,7 @@ private: } rv = MapSECStatus( SEC_DerSignData(arena, &cert->derCert, certDER->data, certDER->len, - privateKey, + privateKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE)); if (NS_FAILED(rv)) { return rv; @@ -239,7 +239,7 @@ private: } // Save the cert in the DB - rv = MapSECStatus(PK11_ImportCert(slot, certFromDER, CK_INVALID_HANDLE, + rv = MapSECStatus(PK11_ImportCert(slot.get(), certFromDER, CK_INVALID_HANDLE, mNickname.get(), false /* unused */)); if (NS_FAILED(rv)) { return rv; @@ -368,21 +368,21 @@ LocalCertService::LoginToKeySlot() nsresult rv; // Get access to key slot - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // If no user password yet, set it an empty one - if (PK11_NeedUserInit(slot)) { - rv = MapSECStatus(PK11_InitPin(slot, "", "")); + if (PK11_NeedUserInit(slot.get())) { + rv = MapSECStatus(PK11_InitPin(slot.get(), "", "")); if (NS_FAILED(rv)) { return rv; } } // If user has a password set, prompt to login - if (PK11_NeedLogin(slot) && !PK11_IsLoggedIn(slot, nullptr)) { + if (PK11_NeedLogin(slot.get()) && !PK11_IsLoggedIn(slot.get(), nullptr)) { // Switching to XPCOM to get the UI prompt that PSM owns nsCOMPtr tokenDB = do_GetService(NS_PK11TOKENDB_CONTRACTID); @@ -452,20 +452,21 @@ LocalCertService::GetLoginPromptRequired(bool* aRequired) nsresult rv; // Get access to key slot - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // If no user password yet, set it an empty one - if (PK11_NeedUserInit(slot)) { - rv = MapSECStatus(PK11_InitPin(slot, "", "")); + if (PK11_NeedUserInit(slot.get())) { + rv = MapSECStatus(PK11_InitPin(slot.get(), "", "")); if (NS_FAILED(rv)) { return rv; } } - *aRequired = PK11_NeedLogin(slot) && !PK11_IsLoggedIn(slot, nullptr); + *aRequired = PK11_NeedLogin(slot.get()) && + !PK11_IsLoggedIn(slot.get(), nullptr); return NS_OK; } diff --git a/security/manager/ssl/PSMContentListener.cpp b/security/manager/ssl/PSMContentListener.cpp index bc6c4c98ec..5ea6ae0b07 100644 --- a/security/manager/ssl/PSMContentListener.cpp +++ b/security/manager/ssl/PSMContentListener.cpp @@ -152,7 +152,7 @@ PSMContentStreamListener::OnStopRequest(nsIRequest* request, // Because importing the cert can spin the event loop (via alerts), we can't // do it here. Do it off the event loop instead. nsCOMPtr r = - NS_NewRunnableMethod(this, &PSMContentStreamListener::ImportCertificate); + NewRunnableMethod(this, &PSMContentStreamListener::ImportCertificate); MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); return NS_OK; @@ -182,17 +182,20 @@ PSMContentStreamListener::ImportCertificate() switch (mType) { case X509_CA_CERT: - certdb->ImportCertificates(reinterpret_cast(mByteData.BeginWriting()), + certdb->ImportCertificates(BitwiseCast( + mByteData.BeginWriting()), mByteData.Length(), mType, ctx); break; case X509_USER_CERT: - certdb->ImportUserCertificate(reinterpret_cast(mByteData.BeginWriting()), + certdb->ImportUserCertificate(BitwiseCast( + mByteData.BeginWriting()), mByteData.Length(), ctx); break; case X509_EMAIL_CERT: - certdb->ImportEmailCertificate(reinterpret_cast(mByteData.BeginWriting()), + certdb->ImportEmailCertificate(BitwiseCast( + mByteData.BeginWriting()), mByteData.Length(), ctx); break; diff --git a/security/manager/ssl/PublicKeyPinningService.cpp b/security/manager/ssl/PublicKeyPinningService.cpp index d9098f1768..1f34c880b6 100644 --- a/security/manager/ssl/PublicKeyPinningService.cpp +++ b/security/manager/ssl/PublicKeyPinningService.cpp @@ -4,16 +4,16 @@ #include "PublicKeyPinningService.h" +#include "RootCertificateTelemetryUtils.h" #include "mozilla/Base64.h" +#include "mozilla/Casting.h" +#include "mozilla/Logging.h" #include "mozilla/Telemetry.h" #include "nsISiteSecurityService.h" #include "nsServiceManagerUtils.h" #include "nsSiteSecurityService.h" #include "nssb64.h" #include "pkix/pkixtypes.h" -#include "mozilla/Logging.h" -#include "RootCertificateTelemetryUtils.h" -#include "ScopedNSSTypes.h" #include "seccomon.h" #include "sechash.h" @@ -40,7 +40,7 @@ GetBase64HashSPKI(const CERTCertificate* cert, nsACString& hashSPKIDigest) return rv; } return Base64Encode(nsDependentCSubstring( - reinterpret_cast(digest.get().data), + BitwiseCast(digest.get().data), digest.get().len), hashSPKIDigest); } @@ -98,7 +98,8 @@ EvalCert(const CERTCertificate* cert, const StaticFingerprints* fingerprints, * dynamicFingerprints array, or to false otherwise. */ static nsresult -EvalChain(const CERTCertList* certList, const StaticFingerprints* fingerprints, +EvalChain(const UniqueCERTCertList& certList, + const StaticFingerprints* fingerprints, const nsTArray* dynamicFingerprints, /*out*/ bool& certListIntersectsPinset) { @@ -135,16 +136,15 @@ EvalChain(const CERTCertList* certList, const StaticFingerprints* fingerprints, Comparator for the is public key pinned host. */ static int -TransportSecurityPreloadCompare(const void *key, const void *entry) { - const char *keyStr = reinterpret_cast(key); - const TransportSecurityPreload *preloadEntry = - reinterpret_cast(entry); +TransportSecurityPreloadCompare(const void* key, const void* entry) { + auto keyStr = static_cast(key); + auto preloadEntry = static_cast(entry); return strcmp(keyStr, preloadEntry->mHost); } nsresult -PublicKeyPinningService::ChainMatchesPinset(const CERTCertList* certList, +PublicKeyPinningService::ChainMatchesPinset(const UniqueCERTCertList& certList, const nsTArray& aSHA256keys, /*out*/ bool& chainMatchesPinset) { @@ -231,7 +231,7 @@ FindPinningInformation(const char* hostname, mozilla::pkix::Time time, // subject public key info data in the list and the most relevant non-expired // pinset for the host or there is no pinning information for the host. static nsresult -CheckPinsForHostname(const CERTCertList* certList, const char* hostname, +CheckPinsForHostname(const UniqueCERTCertList& certList, const char* hostname, bool enforceTestMode, mozilla::pkix::Time time, /*out*/ bool& chainHasValidPins, /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo) @@ -318,7 +318,7 @@ CheckPinsForHostname(const CERTCertList* certList, const char* hostname, } nsresult -PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList, +PublicKeyPinningService::ChainHasValidPins(const UniqueCERTCertList& certList, const char* hostname, mozilla::pkix::Time time, bool enforceTestMode, diff --git a/security/manager/ssl/PublicKeyPinningService.h b/security/manager/ssl/PublicKeyPinningService.h index 601f624e58..f42376b527 100644 --- a/security/manager/ssl/PublicKeyPinningService.h +++ b/security/manager/ssl/PublicKeyPinningService.h @@ -5,8 +5,9 @@ #ifndef PublicKeyPinningService_h #define PublicKeyPinningService_h -#include "cert.h" #include "CertVerifier.h" +#include "ScopedNSSTypes.h" +#include "cert.h" #include "nsString.h" #include "nsTArray.h" #include "pkix/Time.h" @@ -26,7 +27,7 @@ public: * Note: if an alt name is a wildcard, it won't necessarily find a pinset * that would otherwise be valid for it */ - static nsresult ChainHasValidPins(const CERTCertList* certList, + static nsresult ChainHasValidPins(const UniqueCERTCertList& certList, const char* hostname, mozilla::pkix::Time time, bool enforceTestMode, @@ -37,7 +38,7 @@ public: * certificate list and the pins specified in the aSHA256keys array. * Values passed in are assumed to be in base64 encoded form. */ - static nsresult ChainMatchesPinset(const CERTCertList* certList, + static nsresult ChainMatchesPinset(const UniqueCERTCertList& certList, const nsTArray& aSHA256keys, /*out*/ bool& chainMatchesPinset); @@ -61,4 +62,4 @@ public: }} // namespace mozilla::psm -#endif // PublicKeyPinningServiceService_h +#endif // PublicKeyPinningService_h diff --git a/security/manager/ssl/RootCertificateTelemetryUtils.cpp b/security/manager/ssl/RootCertificateTelemetryUtils.cpp index e97b6acbb3..3f9ea3eb62 100644 --- a/security/manager/ssl/RootCertificateTelemetryUtils.cpp +++ b/security/manager/ssl/RootCertificateTelemetryUtils.cpp @@ -58,9 +58,9 @@ RootCABinNumber(const SECItem* cert) digest.get().data[0], digest.get().data[1], digest.get().data[2], digest.get().data[3])); if (mozilla::BinarySearchIf(ROOT_TABLE, 0, ArrayLength(ROOT_TABLE), - BinaryHashSearchArrayComparator( - reinterpret_cast(digest.get().data), digest.get().len), - &idx)) { + BinaryHashSearchArrayComparator(static_cast(digest.get().data), + digest.get().len), + &idx)) { MOZ_LOG(gPublicKeyPinningTelemetryLog, LogLevel::Debug, ("pkpinTelem: Telemetry index was %lu, bin is %d\n", diff --git a/security/manager/ssl/SSLServerCertVerification.cpp b/security/manager/ssl/SSLServerCertVerification.cpp index c2b74c546a..2b4302299a 100644 --- a/security/manager/ssl/SSLServerCertVerification.cpp +++ b/security/manager/ssl/SSLServerCertVerification.cpp @@ -107,6 +107,7 @@ #include "SharedSSLState.h" #include "cert.h" #include "mozilla/Assertions.h" +#include "mozilla/Casting.h" #include "mozilla/Mutex.h" #include "mozilla/Telemetry.h" #include "mozilla/UniquePtr.h" @@ -734,7 +735,7 @@ public: const void* fdForLogging, nsNSSSocketInfo* infoObject, const UniqueCERTCertificate& serverCert, - ScopedCERTCertList& peerCertChain, + const UniqueCERTCertList& peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, Time time, @@ -747,7 +748,7 @@ private: const void* fdForLogging, nsNSSSocketInfo* infoObject, const UniqueCERTCertificate& cert, - CERTCertList* peerCertChain, + UniqueCERTCertList peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, Time time, @@ -756,7 +757,7 @@ private: const void* const mFdForLogging; const RefPtr mInfoObject; const UniqueCERTCertificate mCert; - ScopedCERTCertList mPeerCertChain; + UniqueCERTCertList mPeerCertChain; const uint32_t mProviderFlags; const Time mTime; const PRTime mPRTime; @@ -767,13 +768,13 @@ private: SSLServerCertVerificationJob::SSLServerCertVerificationJob( const RefPtr& certVerifier, const void* fdForLogging, nsNSSSocketInfo* infoObject, const UniqueCERTCertificate& cert, - CERTCertList* peerCertChain, SECItem* stapledOCSPResponse, + UniqueCERTCertList peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, Time time, PRTime prtime) : mCertVerifier(certVerifier) , mFdForLogging(fdForLogging) , mInfoObject(infoObject) , mCert(CERT_DupCertificate(cert.get())) - , mPeerCertChain(peerCertChain) + , mPeerCertChain(Move(peerCertChain)) , mProviderFlags(providerFlags) , mTime(time) , mPRTime(prtime) @@ -893,7 +894,7 @@ TryMatchingWildcardSubjectAltName(const char* commonName, // certList consists of a validated certificate chain. The end-entity // certificate is first and the root (trust anchor) is last. void -GatherBaselineRequirementsTelemetry(const ScopedCERTCertList& certList) +GatherBaselineRequirementsTelemetry(const UniqueCERTCertList& certList) { CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList); CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); @@ -963,7 +964,8 @@ GatherBaselineRequirementsTelemetry(const ScopedCERTCertList& certList) do { nsAutoCString altName; if (currentName->type == certDNSName) { - altName.Assign(reinterpret_cast(currentName->name.other.data), + altName.Assign(BitwiseCast( + currentName->name.other.data), currentName->name.other.len); nsDependentCString altNameWithoutWildcard(altName, 0); if (StringBeginsWith(altNameWithoutWildcard, NS_LITERAL_CSTRING("*."))) { @@ -1058,7 +1060,7 @@ GatherBaselineRequirementsTelemetry(const ScopedCERTCertList& certList) // Gather telemetry on whether the end-entity cert for a server has the // required TLS Server Authentication EKU, or any others void -GatherEKUTelemetry(const ScopedCERTCertList& certList) +GatherEKUTelemetry(const UniqueCERTCertList& certList) { CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList); CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); @@ -1140,7 +1142,7 @@ GatherEKUTelemetry(const ScopedCERTCertList& certList) // If the root is a built-in root, then the telemetry makes a count // by root. Roots that are not built-in are counted in one bin. void -GatherRootCATelemetry(const ScopedCERTCertList& certList) +GatherRootCATelemetry(const UniqueCERTCertList& certList) { CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); PR_ASSERT(rootNode); @@ -1166,7 +1168,7 @@ const uint64_t ONE_YEAR_IN_WEEKS = 52; // Gathers telemetry on the certificate lifetimes we observe in the wild void -GatherEndEntityTelemetry(const ScopedCERTCertList& certList) +GatherEndEntityTelemetry(const UniqueCERTCertList& certList) { CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList); PR_ASSERT(endEntityNode); @@ -1207,7 +1209,7 @@ GatherEndEntityTelemetry(const ScopedCERTCertList& certList) // There are various things that we want to measure about certificate // chains that we accept. This is a single entry point for all of them. void -GatherSuccessfulValidationTelemetry(const ScopedCERTCertList& certList) +GatherSuccessfulValidationTelemetry(const UniqueCERTCertList& certList) { GatherBaselineRequirementsTelemetry(certList); GatherEKUTelemetry(certList); @@ -1215,11 +1217,12 @@ GatherSuccessfulValidationTelemetry(const ScopedCERTCertList& certList) GatherEndEntityTelemetry(certList); } +// Note: Takes ownership of |peerCertChain| if SECSuccess is not returned. SECStatus AuthCertificate(CertVerifier& certVerifier, nsNSSSocketInfo* infoObject, const UniqueCERTCertificate& cert, - ScopedCERTCertList& peerCertChain, + UniqueCERTCertList& peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, Time time) @@ -1235,7 +1238,7 @@ AuthCertificate(CertVerifier& certVerifier, !(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE); SECOidTag evOidPolicy; - ScopedCERTCertList certList; + UniqueCERTCertList certList; CertVerifier::OCSPStaplingStatus ocspStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED; KeySizeStatus keySizeStatus = KeySizeStatus::NeverChecked; @@ -1342,9 +1345,8 @@ AuthCertificate(CertVerifier& certVerifier, if (rv != SECSuccess) { // Certificate validation failed; store the peer certificate chain on - // infoObject so it can be used for error reporting. Note: infoObject - // indirectly takes ownership of peerCertChain. - infoObject->SetFailedCertChain(peerCertChain); + // infoObject so it can be used for error reporting. + infoObject->SetFailedCertChain(Move(peerCertChain)); PR_SetError(savedErrorCode, 0); } @@ -1357,7 +1359,7 @@ SSLServerCertVerificationJob::Dispatch( const void* fdForLogging, nsNSSSocketInfo* infoObject, const UniqueCERTCertificate& serverCert, - ScopedCERTCertList& peerCertChain, + const UniqueCERTCertList& peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, Time time, @@ -1375,11 +1377,16 @@ SSLServerCertVerificationJob::Dispatch( // We can safely skip checking if NSS has already shut down here since we're // in the middle of verifying a certificate. nsNSSShutDownPreventionLock lock; - CERTCertList* peerCertChainCopy = nsNSSCertList::DupCertList(peerCertChain, lock); + UniqueCERTCertList peerCertChainCopy = + nsNSSCertList::DupCertList(peerCertChain, lock); + if (!peerCertChainCopy) { + PR_SetError(SEC_ERROR_NO_MEMORY, 0); + return SECFailure; + } RefPtr job( new SSLServerCertVerificationJob(certVerifier, fdForLogging, infoObject, - serverCert, peerCertChainCopy, + serverCert, Move(peerCertChainCopy), stapledOCSPResponse, providerFlags, time, prtime)); @@ -1433,6 +1440,8 @@ SSLServerCertVerificationJob::Run() SECStatus rv = AuthCertificate(*mCertVerifier, mInfoObject, mCert, mPeerCertChain, mStapledOCSPResponse, mProviderFlags, mTime); + MOZ_ASSERT(mPeerCertChain || rv != SECSuccess, + "AuthCertificate() should take ownership of chain on failure"); if (rv == SECSuccess) { uint32_t interval = (uint32_t) ((TimeStamp::Now() - mJobStartTime).ToMilliseconds()); RefPtr restart( @@ -1533,7 +1542,11 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer) } // Get the peer certificate chain for error reporting - ScopedCERTCertList peerCertChain(SSL_PeerCertificateChain(fd)); + UniqueCERTCertList peerCertChain(SSL_PeerCertificateChain(fd)); + if (!peerCertChain) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return SECFailure; + } socketInfo->SetFullHandshake(); @@ -1601,6 +1614,8 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer) SECStatus rv = AuthCertificate(*certVerifier, socketInfo, serverCert, peerCertChain, stapledOCSPResponse, providerFlags, now); + MOZ_ASSERT(peerCertChain || rv != SECSuccess, + "AuthCertificate() should take ownership of chain on failure"); if (rv == SECSuccess) { Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1); return SECSuccess; diff --git a/security/manager/ssl/ScopedNSSTypes.h b/security/manager/ssl/ScopedNSSTypes.h index 2917c8eeee..ac02ed672b 100644 --- a/security/manager/ssl/ScopedNSSTypes.h +++ b/security/manager/ssl/ScopedNSSTypes.h @@ -16,6 +16,7 @@ #include "cms.h" #include "cryptohi.h" #include "keyhi.h" +#include "mozilla/Casting.h" #include "mozilla/Likely.h" #include "mozilla/Scoped.h" #include "mozilla/UniquePtr.h" @@ -37,21 +38,23 @@ namespace mozilla { +// Deprecated: Use something like |mozilla::BitwiseCast(p)| +// instead. // It is very common to cast between char* and uint8_t* when doing crypto stuff. // Here, we provide more type-safe wrappers around reinterpret_cast so you don't // shoot yourself in the foot by reinterpret_casting completely unrelated types. -inline char * -char_ptr_cast(uint8_t * p) { return reinterpret_cast(p); } +inline char* +char_ptr_cast(uint8_t* p) { return BitwiseCast(p); } -inline const char * -char_ptr_cast(const uint8_t * p) { return reinterpret_cast(p); } +inline const char* +char_ptr_cast(const uint8_t* p) { return BitwiseCast(p); } -inline uint8_t * -uint8_t_ptr_cast(char * p) { return reinterpret_cast(p); } +inline uint8_t* +uint8_t_ptr_cast(char* p) { return BitwiseCast(p); } -inline const uint8_t * -uint8_t_ptr_cast(const char * p) { return reinterpret_cast(p); } +inline const uint8_t* +uint8_t_ptr_cast(const char* p) { return BitwiseCast(p); } // NSPR APIs use PRStatus/PR_GetError and NSS APIs use SECStatus/PR_GetError to // report success/failure. This function makes it more convenient and *safer* @@ -86,9 +89,6 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertificateList, MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertificateRequest, CERTCertificateRequest, CERT_DestroyCertificateRequest) -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertList, - CERTCertList, - CERT_DestroyCertList) MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTName, CERTName, CERT_DestroyName) diff --git a/security/manager/ssl/TransportSecurityInfo.cpp b/security/manager/ssl/TransportSecurityInfo.cpp index 58476bf5cc..215f3628c6 100644 --- a/security/manager/ssl/TransportSecurityInfo.cpp +++ b/security/manager/ssl/TransportSecurityInfo.cpp @@ -6,29 +6,30 @@ #include "TransportSecurityInfo.h" -#include "pkix/pkixtypes.h" -#include "nsNSSComponent.h" -#include "nsIWebProgressListener.h" -#include "nsNSSCertificate.h" -#include "nsIX509CertValidity.h" -#include "nsIDateTimeFormat.h" +#include "PSMRunnable.h" +#include "mozilla/Casting.h" +#include "nsComponentManagerUtils.h" +#include "nsIArray.h" #include "nsICertOverrideService.h" +#include "nsIDateTimeFormat.h" #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" +#include "nsIWebProgressListener.h" +#include "nsIX509CertValidity.h" #include "nsNSSCertHelper.h" -#include "nsIArray.h" -#include "nsComponentManagerUtils.h" +#include "nsNSSCertificate.h" +#include "nsNSSComponent.h" #include "nsReadableUtils.h" #include "nsServiceManagerUtils.h" #include "nsXULAppAPI.h" -#include "PSMRunnable.h" #include "mozilla/net/DNS.h" +#include "pkix/pkixtypes.h" #include "secerr.h" //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal //reports when doing SSL read/write - + //#define DUMP_BUFFER //Enable this define along with //DEBUG_SSL_VERBOSE to dump SSL //read/write buffer to a log. @@ -422,7 +423,7 @@ TransportSecurityInfo::Read(nsIObjectInputStream* stream) if (NS_FAILED(rv)) { return rv; } - mSSLStatus = reinterpret_cast(supports.get()); + mSSLStatus = BitwiseCast(supports.get()); nsCOMPtr failedCertChainSupports; rv = NS_ReadOptionalObject(stream, true, getter_AddRefs(failedCertChainSupports)); @@ -651,9 +652,9 @@ GetSubjectAltNames(CERTCertificate* nssCert, nsString& allNames) switch (current->type) { case certDNSName: { - nsDependentCSubstring nameFromCert(reinterpret_cast - (current->name.other.data), - current->name.other.len); + nsDependentCSubstring nameFromCert(BitwiseCast( + current->name.other.data), + current->name.other.len); // dNSName fields are defined as type IA5String and thus should // be limited to ASCII characters. if (IsASCII(nameFromCert)) { @@ -1079,16 +1080,15 @@ TransportSecurityInfo::GetFailedCertChain(nsIX509CertList** _result) } nsresult -TransportSecurityInfo::SetFailedCertChain(ScopedCERTCertList& certList) +TransportSecurityInfo::SetFailedCertChain(UniqueCERTCertList certList) { nsNSSShutDownPreventionLock lock; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } - nsCOMPtr comCertList; // nsNSSCertList takes ownership of certList - mFailedCertChain = new nsNSSCertList(certList, lock); + mFailedCertChain = new nsNSSCertList(Move(certList), lock); return NS_OK; } diff --git a/security/manager/ssl/TransportSecurityInfo.h b/security/manager/ssl/TransportSecurityInfo.h index 1f6e1f8416..2910167f17 100644 --- a/security/manager/ssl/TransportSecurityInfo.h +++ b/security/manager/ssl/TransportSecurityInfo.h @@ -4,8 +4,8 @@ * 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 _MOZILLA_PSM_TRANSPORTSECURITYINFO_H -#define _MOZILLA_PSM_TRANSPORTSECURITYINFO_H +#ifndef TransportSecurityInfo_h +#define TransportSecurityInfo_h #include "ScopedNSSTypes.h" #include "certt.h" @@ -70,13 +70,13 @@ public: void SetCanceled(PRErrorCode errorCode, ::mozilla::psm::SSLErrorMessageType errorMessageType); - + /* Set SSL Status values */ nsresult SetSSLStatus(nsSSLStatus *aSSLStatus); nsSSLStatus* SSLStatus() { return mSSLStatus; } void SetStatusErrorBits(nsNSSCertificate* cert, uint32_t collected_errors); - nsresult SetFailedCertChain(ScopedCERTCertList& certList); + nsresult SetFailedCertChain(UniqueCERTCertList certList); private: mutable ::mozilla::Mutex mMutex; @@ -161,4 +161,4 @@ private: { 0x16786594, 0x0296, 0x4471, \ { 0x80, 0x96, 0x8f, 0x84, 0x49, 0x7c, 0xa4, 0x28 } } -#endif /* _MOZILLA_PSM_TRANSPORTSECURITYINFO_H */ +#endif // TransportSecurityInfo_h diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp index ee8031e4af..ca1844c673 100644 --- a/security/manager/ssl/nsCertOverrideService.cpp +++ b/security/manager/ssl/nsCertOverrideService.cpp @@ -371,27 +371,24 @@ nsCertOverrideService::RememberValidityOverride(const nsACString& aHostName, return NS_ERROR_FAILURE; } - char* nickname = DefaultServerNicknameForCert(nsscert.get()); - if (!aTemporary && nickname && *nickname) - { - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + nsAutoCString nickname; + nsresult rv = DefaultServerNicknameForCert(nsscert.get(), nickname); + if (!aTemporary && NS_SUCCEEDED(rv)) { + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { - PR_Free(nickname); return NS_ERROR_FAILURE; } - - SECStatus srv = PK11_ImportCert(slot, nsscert.get(), CK_INVALID_HANDLE, - nickname, false); + + SECStatus srv = PK11_ImportCert(slot.get(), nsscert.get(), CK_INVALID_HANDLE, + nickname.get(), false); if (srv != SECSuccess) { - PR_Free(nickname); return NS_ERROR_FAILURE; } } - PR_FREEIF(nickname); nsAutoCString fpStr; - nsresult rv = GetCertFingerprintByOidTag(nsscert.get(), - mOidTagForStoringNewHashes, fpStr); + rv = GetCertFingerprintByOidTag(nsscert.get(), mOidTagForStoringNewHashes, + fpStr); if (NS_FAILED(rv)) return rv; diff --git a/security/manager/ssl/nsCertPicker.cpp b/security/manager/ssl/nsCertPicker.cpp index 987e374293..7cb04416e2 100644 --- a/security/manager/ssl/nsCertPicker.cpp +++ b/security/manager/ssl/nsCertPicker.cpp @@ -64,14 +64,13 @@ NS_IMETHODIMP nsCertPicker::PickByUsage(nsIInterfaceRequestor *ctx, { // Iterate over all certs. This assures that user is logged in to all hardware tokens. nsCOMPtr ctx = new PipUIContext(); - ScopedCERTCertList allcerts(PK11_ListCerts(PK11CertListUnique, ctx)); + UniqueCERTCertList allcerts(PK11_ListCerts(PK11CertListUnique, ctx)); } /* find all user certs that are valid for the specified usage */ /* note that we are allowing expired certs in this list */ - - ScopedCERTCertList certList( - CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), + UniqueCERTCertList certList( + CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), (SECCertUsage)certUsage, !allowDuplicateNicknames, !allowInvalid, @@ -106,8 +105,7 @@ NS_IMETHODIMP nsCertPicker::PickByUsage(nsIInterfaceRequestor *ctx, } } - UniqueCERTCertNicknames nicknames( - getNSSCertNicknamesFromCertList(certList.get())); + UniqueCERTCertNicknames nicknames(getNSSCertNicknamesFromCertList(certList)); if (!nicknames) { return NS_ERROR_NOT_AVAILABLE; } diff --git a/security/manager/ssl/nsCertTree.cpp b/security/manager/ssl/nsCertTree.cpp index 2729be2696..25aa04a2c3 100644 --- a/security/manager/ssl/nsCertTree.cpp +++ b/security/manager/ssl/nsCertTree.cpp @@ -182,10 +182,10 @@ nsCertTree::FreeCertArray() mDispInfo.Clear(); } -CompareCacheHashEntry * -nsCertTree::getCacheEntry(void *cache, void *aCert) +CompareCacheHashEntry* +nsCertTree::getCacheEntry(void* cache, void* aCert) { - PLDHashTable &aCompareCache = *reinterpret_cast(cache); + PLDHashTable& aCompareCache = *static_cast(cache); auto entryPtr = static_cast (aCompareCache.Add(aCert, fallible)); return entryPtr ? entryPtr->entry : nullptr; @@ -584,7 +584,7 @@ nsCertTree::GetCertsByType(uint32_t aType, { nsNSSShutDownPreventionLock locker; nsCOMPtr cxt = new PipUIContext(); - ScopedCERTCertList certList(PK11_ListCerts(PK11CertListUnique, cxt)); + UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, cxt)); return GetCertsByTypeFromCertList(certList.get(), aType, aCertCmpFn, aCertCmpFnArg); } @@ -605,7 +605,7 @@ nsCertTree::GetCertsByTypeFromCache(nsIX509CertList *aCache, // more encapsulated types that handled NSS shutdown themselves, we wouldn't // be having these kinds of problems. nsNSSShutDownPreventionLock locker; - CERTCertList *certList = reinterpret_cast(aCache->GetRawCertList()); + CERTCertList* certList = aCache->GetRawCertList(); if (!certList) return NS_ERROR_FAILURE; return GetCertsByTypeFromCertList(certList, aType, aCertCmpFn, aCertCmpFnArg); diff --git a/security/manager/ssl/nsDataSignatureVerifier.cpp b/security/manager/ssl/nsDataSignatureVerifier.cpp index b2dc945e30..b62d09490e 100644 --- a/security/manager/ssl/nsDataSignatureVerifier.cpp +++ b/security/manager/ssl/nsDataSignatureVerifier.cpp @@ -7,6 +7,7 @@ #include "cms.h" #include "cryptohi.h" #include "keyhi.h" +#include "mozilla/Casting.h" #include "mozilla/unused.h" #include "nsCOMPtr.h" #include "nsNSSComponent.h" @@ -108,7 +109,8 @@ nsDataSignatureVerifier::VerifyData(const nsACString& aData, // Perform the final verification DER_ConvertBitString(&(sigData.signature)); srv = VFY_VerifyDataWithAlgorithmID( - reinterpret_cast(PromiseFlatCString(aData).get()), + BitwiseCast( + PromiseFlatCString(aData).get()), aData.Length(), publicKey.get(), &(sigData.signature), &(sigData.signatureAlgorithm), nullptr, nullptr); @@ -160,7 +162,7 @@ VerifyCMSDetachedSignatureIncludingCertificate( // signedData is non-owning NSSCMSSignedData* signedData = - reinterpret_cast(NSS_CMSContentInfo_GetContent(cinfo)); + static_cast(NSS_CMSContentInfo_GetContent(cinfo)); if (!signedData) { return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; } @@ -242,7 +244,7 @@ namespace { struct VerifyCertificateContext { nsCOMPtr signingCert; - ScopedCERTCertList builtChain; + UniqueCERTCertList builtChain; }; static nsresult @@ -254,7 +256,7 @@ VerifyCertificate(CERTCertificate* cert, void* voidContext, void* pinArg) } VerifyCertificateContext* context = - reinterpret_cast(voidContext); + static_cast(voidContext); nsCOMPtr xpcomCert(nsNSSCertificate::Create(cert)); if (!xpcomCert) { @@ -304,7 +306,7 @@ nsDataSignatureVerifier::VerifySignature(const char* aRSABuf, SECItem buffer = { siBuffer, - reinterpret_cast(const_cast(aRSABuf)), + BitwiseCast(aRSABuf), aRSABufLen }; diff --git a/security/manager/ssl/nsIX509CertList.idl b/security/manager/ssl/nsIX509CertList.idl index e0be66fb68..63f1c5e384 100644 --- a/security/manager/ssl/nsIX509CertList.idl +++ b/security/manager/ssl/nsIX509CertList.idl @@ -7,16 +7,23 @@ interface nsISimpleEnumerator; interface nsIX509Cert; +%{C++ +typedef struct CERTCertListStr CERTCertList; +%} +[ptr] native CERTCertListPtr(CERTCertList); + [scriptable, uuid(ae74cda5-cd2f-473f-96f5-f0b7fff62c68)] interface nsIX509CertList : nsISupports { void addCert(in nsIX509Cert cert); void deleteCert(in nsIX509Cert cert); nsISimpleEnumerator getEnumerator(); - /* getRawCertList MUST be called only from functions where - * the nssShutdownPreventionLock has been adquired. + /** + * Returns the raw, backing cert list. + * Must be called only from functions where an nsNSSShutDownPreventionLock + * has been acquired. */ - [notxpcom, noscript] voidPtr getRawCertList(); + [notxpcom, noscript] CERTCertListPtr getRawCertList(); /** * Test whether two certificate list instances represent the same diff --git a/security/manager/ssl/nsKeyModule.cpp b/security/manager/ssl/nsKeyModule.cpp index 6b0f80ac45..7c5fcf5c98 100644 --- a/security/manager/ssl/nsKeyModule.cpp +++ b/security/manager/ssl/nsKeyModule.cpp @@ -137,7 +137,7 @@ nsKeyObjectFactory::KeyFromString(int16_t aAlgorithm, const nsACString& aKey, keyItem.data = (unsigned char*)flatKey.get(); keyItem.len = flatKey.Length(); - ScopedPK11SlotInfo slot(PK11_GetBestSlot(cipherMech, nullptr)); + UniquePK11SlotInfo slot(PK11_GetBestSlot(cipherMech, nullptr)); if (!slot) { return NS_ERROR_FAILURE; } diff --git a/security/manager/ssl/nsNSSCallbacks.cpp b/security/manager/ssl/nsNSSCallbacks.cpp index 62241c1abe..986665eb27 100644 --- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -6,6 +6,8 @@ #include "nsNSSCallbacks.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Casting.h" #include "mozilla/TimeStamp.h" #include "nsContentUtils.h" #include "nsICertOverrideService.h" @@ -176,7 +178,7 @@ struct nsCancelHTTPDownloadEvent : Runnable { Result nsNSSHttpServerSession::createSessionFcn(const char* host, uint16_t portnum, - SEC_HTTP_SERVER_SESSION* pSession) + /*out*/ nsNSSHttpServerSession** pSession) { if (!host || !pSession) { return Result::FATAL_ERROR_INVALID_ARGS; @@ -195,23 +197,18 @@ nsNSSHttpServerSession::createSessionFcn(const char* host, } Result -nsNSSHttpRequestSession::createFcn(SEC_HTTP_SERVER_SESSION session, +nsNSSHttpRequestSession::createFcn(const nsNSSHttpServerSession* session, const char* http_protocol_variant, const char* path_and_query_string, const char* http_request_method, const PRIntervalTime timeout, - SEC_HTTP_REQUEST_SESSION* pRequest) + /*out*/ nsNSSHttpRequestSession** pRequest) { if (!session || !http_protocol_variant || !path_and_query_string || !http_request_method || !pRequest) { return Result::FATAL_ERROR_INVALID_ARGS; } - nsNSSHttpServerSession* hss = static_cast(session); - if (!hss) { - return Result::FATAL_ERROR_INVALID_ARGS; - } - nsNSSHttpRequestSession* rs = new nsNSSHttpRequestSession; if (!rs) { return Result::FATAL_ERROR_NO_MEMORY; @@ -228,14 +225,14 @@ nsNSSHttpRequestSession::createFcn(SEC_HTTP_SERVER_SESSION session, rs->mURL.Assign(http_protocol_variant); rs->mURL.AppendLiteral("://"); - rs->mURL.Append(hss->mHost); + rs->mURL.Append(session->mHost); rs->mURL.Append(':'); - rs->mURL.AppendInt(hss->mPort); + rs->mURL.AppendInt(session->mPort); rs->mURL.Append(path_and_query_string); rs->mRequestMethod = http_request_method; - *pRequest = (void*)rs; + *pRequest = rs; return Success; } @@ -830,16 +827,17 @@ PreliminaryHandshakeDone(PRFileDesc* fd) unsigned char npnbuf[256]; unsigned int npnlen; - if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess) { + if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, + AssertedCast(ArrayLength(npnbuf))) + == SECSuccess) { if (state == SSL_NEXT_PROTO_NEGOTIATED || state == SSL_NEXT_PROTO_SELECTED) { - infoObject->SetNegotiatedNPN(reinterpret_cast(npnbuf), npnlen); - } - else { + infoObject->SetNegotiatedNPN(BitwiseCast(npnbuf), + npnlen); + } else { infoObject->SetNegotiatedNPN(nullptr, 0); } - } - else { + } else { infoObject->SetNegotiatedNPN(nullptr, 0); } diff --git a/security/manager/ssl/nsNSSCallbacks.h b/security/manager/ssl/nsNSSCallbacks.h index 8ec6f95d1d..6c123098ae 100644 --- a/security/manager/ssl/nsNSSCallbacks.h +++ b/security/manager/ssl/nsNSSCallbacks.h @@ -84,7 +84,7 @@ public: static Result createSessionFcn(const char* host, uint16_t portnum, - SEC_HTTP_SERVER_SESSION* pSession); + /*out*/ nsNSSHttpServerSession** pSession); }; class nsNSSHttpRequestSession @@ -95,12 +95,12 @@ protected: public: typedef mozilla::pkix::Result Result; - static Result createFcn(SEC_HTTP_SERVER_SESSION session, + static Result createFcn(const nsNSSHttpServerSession* session, const char* httpProtocolVariant, const char* pathAndQueryString, const char* httpRequestMethod, const PRIntervalTime timeout, - SEC_HTTP_REQUEST_SESSION* pRequest); + /*out*/ nsNSSHttpRequestSession** pRequest); Result setPostDataFcn(const char* httpData, const uint32_t httpDataLen, @@ -147,17 +147,17 @@ public: static Result createSessionFcn(const char* host, uint16_t portnum, - SEC_HTTP_SERVER_SESSION* pSession) + /*out*/ nsNSSHttpServerSession** pSession) { return nsNSSHttpServerSession::createSessionFcn(host, portnum, pSession); } - static Result createFcn(SEC_HTTP_SERVER_SESSION session, + static Result createFcn(const nsNSSHttpServerSession* session, const char* httpProtocolVariant, const char* pathAndQueryString, const char* httpRequestMethod, const PRIntervalTime timeout, - SEC_HTTP_REQUEST_SESSION* pRequest) + /*out*/ nsNSSHttpRequestSession** pRequest) { return nsNSSHttpRequestSession::createFcn(session, httpProtocolVariant, pathAndQueryString, @@ -165,16 +165,15 @@ public: pRequest); } - static Result setPostDataFcn(SEC_HTTP_REQUEST_SESSION request, + static Result setPostDataFcn(nsNSSHttpRequestSession* request, const char* httpData, const uint32_t httpDataLen, const char* httpContentType) { - return static_cast(request) - ->setPostDataFcn(httpData, httpDataLen, httpContentType); + return request->setPostDataFcn(httpData, httpDataLen, httpContentType); } - static Result trySendAndReceiveFcn(SEC_HTTP_REQUEST_SESSION request, + static Result trySendAndReceiveFcn(nsNSSHttpRequestSession* request, PRPollDesc** pPollDesc, uint16_t* httpResponseCode, const char** httpResponseContentType, @@ -182,10 +181,10 @@ public: const char** httpResponseData, uint32_t* httpResponseDataLen) { - return static_cast(request) - ->trySendAndReceiveFcn(pPollDesc, httpResponseCode, - httpResponseContentType, httpResponseHeaders, - httpResponseData, httpResponseDataLen); + return request->trySendAndReceiveFcn(pPollDesc, httpResponseCode, + httpResponseContentType, + httpResponseHeaders, + httpResponseData, httpResponseDataLen); } }; diff --git a/security/manager/ssl/nsNSSCertHelper.cpp b/security/manager/ssl/nsNSSCertHelper.cpp index 50bfde108d..5da7b7a7c5 100644 --- a/security/manager/ssl/nsNSSCertHelper.cpp +++ b/security/manager/ssl/nsNSSCertHelper.cpp @@ -6,7 +6,7 @@ #include -#include "ScopedNSSTypes.h" +#include "mozilla/Casting.h" #include "mozilla/net/DNS.h" #include "mozilla/Snprintf.h" #include "mozilla/UniquePtr.h" @@ -102,7 +102,7 @@ ProcessVersion(SECItem* versionItem, nsINSSComponent* nssComponent, if (versionItem->len != 1) { return NS_ERROR_FAILURE; } - version = *reinterpret_cast(versionItem->data); + version = *BitwiseCast(versionItem->data); } else { // If there is no version present in the cert, then RFC 5280 says we // default to v1 (0). @@ -2065,8 +2065,8 @@ getCertType(CERTCertificate *cert) return nsIX509Cert::UNKNOWN_CERT; } -CERTCertNicknames * -getNSSCertNicknamesFromCertList(CERTCertList *certList) +CERTCertNicknames* +getNSSCertNicknamesFromCertList(const UniqueCERTCertList& certList) { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); @@ -2091,10 +2091,9 @@ getNSSCertNicknamesFromCertList(CERTCertList *certList) NS_ConvertUTF16toUTF8 aUtf8ExpiredString(expiredStringLeadingSpace); NS_ConvertUTF16toUTF8 aUtf8NotYetValidString(notYetValidStringLeadingSpace); - return CERT_NicknameStringsFromCertList(certList, + return CERT_NicknameStringsFromCertList(certList.get(), const_cast(aUtf8ExpiredString.get()), const_cast(aUtf8NotYetValidString.get())); - } nsresult diff --git a/security/manager/ssl/nsNSSCertHelper.h b/security/manager/ssl/nsNSSCertHelper.h index 3f30ad7ccf..1c84ea05c8 100644 --- a/security/manager/ssl/nsNSSCertHelper.h +++ b/security/manager/ssl/nsNSSCertHelper.h @@ -2,25 +2,26 @@ * 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 _NSNSSCERTHELPER_H_ -#define _NSNSSCERTHELPER_H_ +#ifndef nsNSSCertHelper_h +#define nsNSSCertHelper_h #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif +#include "ScopedNSSTypes.h" #include "certt.h" #include "nsString.h" uint32_t getCertType(CERTCertificate *cert); -CERTCertNicknames * -getNSSCertNicknamesFromCertList(CERTCertList *certList); +CERTCertNicknames* +getNSSCertNicknamesFromCertList(const mozilla::UniqueCERTCertList& certList); nsresult GetCertFingerprintByOidTag(CERTCertificate* nsscert, SECOidTag aOidTag, nsCString &fp); -#endif +#endif // nsNSSCertHelper_h diff --git a/security/manager/ssl/nsNSSCertificate.cpp b/security/manager/ssl/nsNSSCertificate.cpp index 1ccc243ab7..843b50db12 100644 --- a/security/manager/ssl/nsNSSCertificate.cpp +++ b/security/manager/ssl/nsNSSCertificate.cpp @@ -10,6 +10,7 @@ #include "NSSCertDBTrustDomain.h" #include "certdb.h" #include "mozilla/Base64.h" +#include "mozilla/Casting.h" #include "mozilla/unused.h" #include "nsArray.h" #include "nsCOMPtr.h" @@ -532,12 +533,14 @@ nsNSSCertificate::GetDbKey(const UniqueCERTCertificate& cert, nsACString& aDbKey const char leadingZeroes[] = {0, 0, 0, 0, 0, 0, 0, 0}; buf.Append(leadingZeroes, sizeof(leadingZeroes)); uint32_t serialNumberLen = htonl(cert->serialNumber.len); - buf.Append(reinterpret_cast(&serialNumberLen), sizeof(uint32_t)); + buf.Append(BitwiseCast(&serialNumberLen), + sizeof(uint32_t)); uint32_t issuerLen = htonl(cert->derIssuer.len); - buf.Append(reinterpret_cast(&issuerLen), sizeof(uint32_t)); - buf.Append(reinterpret_cast(cert->serialNumber.data), + buf.Append(BitwiseCast(&issuerLen), + sizeof(uint32_t)); + buf.Append(BitwiseCast(cert->serialNumber.data), cert->serialNumber.len); - buf.Append(reinterpret_cast(cert->derIssuer.data), + buf.Append(BitwiseCast(cert->derIssuer.data), cert->derIssuer.len); return Base64Encode(buf, aDbKey); @@ -841,10 +844,10 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain) mozilla::pkix::Time now(mozilla::pkix::Now()); - ScopedCERTCertList nssChain; RefPtr certVerifier(GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); + UniqueCERTCertList nssChain; // We want to test all usages, but we start with server because most of the // time Firefox users care about server certs. if (certVerifier->VerifyCert(mCert.get(), certificateUsageSSLServer, now, @@ -884,16 +887,15 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain) } if (!nssChain) { - // There is not verified path for the chain, howeever we still want to + // There is not verified path for the chain, however we still want to // present to the user as much of a possible chain as possible, in the case // where there was a problem with the cert or the issuers. MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("pipnss: getchain :CertVerify failed to get chain for '%s'\n", mCert->nickname)); - nssChain = CERT_GetCertChainFromCert(mCert.get(), PR_Now(), - certUsageSSLClient); - } - + nssChain = UniqueCERTCertList( + CERT_GetCertChainFromCert(mCert.get(), PR_Now(), certUsageSSLClient)); + } if (!nssChain) { return NS_ERROR_FAILURE; } @@ -1102,7 +1104,7 @@ nsNSSCertificate::GetSha256SubjectPublicKeyInfoDigest(nsACString& aSha256SPKIDig return rv; } rv = Base64Encode(nsDependentCSubstring( - reinterpret_cast (digest.get().data), + BitwiseCast(digest.get().data), digest.get().len), aSha256SPKIDigest); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -1405,7 +1407,7 @@ nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV) uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY | mozilla::psm::CertVerifier::FLAG_MUST_BE_EV; - ScopedCERTCertList unusedBuiltChain; + UniqueCERTCertList unusedBuiltChain; SECStatus rv = certVerifier->VerifyCert(mCert.get(), certificateUsageSSLServer, mozilla::pkix::Now(), nullptr /* XXX pinarg */, @@ -1479,9 +1481,9 @@ namespace mozilla { SECStatus ConstructCERTCertListFromReversedDERArray( const mozilla::pkix::DERArray& certArray, - /*out*/ ScopedCERTCertList& certList) + /*out*/ UniqueCERTCertList& certList) { - certList = CERT_NewCertList(); + certList = UniqueCERTCertList(CERT_NewCertList()); if (!certList) { return SECFailure; } @@ -1519,19 +1521,19 @@ NS_IMPL_ISUPPORTS_CI(nsNSSCertList, nsIX509CertList, nsISerializable) -nsNSSCertList::nsNSSCertList(ScopedCERTCertList& certList, +nsNSSCertList::nsNSSCertList(UniqueCERTCertList certList, const nsNSSShutDownPreventionLock& proofOfLock) { if (certList) { - mCertList = certList.forget(); + mCertList = Move(certList); } else { - mCertList = CERT_NewCertList(); + mCertList = UniqueCERTCertList(CERT_NewCertList()); } } nsNSSCertList::nsNSSCertList() { - mCertList = CERT_NewCertList(); + mCertList = UniqueCERTCertList(CERT_NewCertList()); } nsNSSCertList::~nsNSSCertList() @@ -1551,9 +1553,7 @@ void nsNSSCertList::virtualDestroyNSSReference() void nsNSSCertList::destructorSafeDestroyNSSReference() { - if (mCertList) { - mCertList = nullptr; - } + mCertList = nullptr; } NS_IMETHODIMP @@ -1612,30 +1612,37 @@ nsNSSCertList::DeleteCert(nsIX509Cert* aCert) return NS_OK; // XXX Should we fail if we couldn't find it? } -CERTCertList* -nsNSSCertList::DupCertList(CERTCertList* aCertList, +UniqueCERTCertList +nsNSSCertList::DupCertList(const UniqueCERTCertList& certList, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { - if (!aCertList) { + if (!certList) { return nullptr; } - CERTCertList* newList = CERT_NewCertList(); - + UniqueCERTCertList newList(CERT_NewCertList()); if (!newList) { return nullptr; } - CERTCertListNode* node; - for (node = CERT_LIST_HEAD(aCertList); !CERT_LIST_END(node, aCertList); - node = CERT_LIST_NEXT(node)) { - CERTCertificate* cert = CERT_DupCertificate(node->cert); - CERT_AddCertToListTail(newList, cert); + for (CERTCertListNode* node = CERT_LIST_HEAD(certList); + !CERT_LIST_END(node, certList); + node = CERT_LIST_NEXT(node)) { + UniqueCERTCertificate cert(CERT_DupCertificate(node->cert)); + if (!cert) { + return nullptr; + } + + if (CERT_AddCertToListTail(newList.get(), cert.get()) != SECSuccess) { + return nullptr; + } + + Unused << cert.release(); // Ownership transferred to the cert list. } return newList; } -void* +CERTCertList* nsNSSCertList::GetRawCertList() { // This function should only be called after acquiring a @@ -1740,8 +1747,13 @@ nsNSSCertList::GetEnumerator(nsISimpleEnumerator** _retval) if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } + + if (!mCertList) { + return NS_ERROR_FAILURE; + } + nsCOMPtr enumerator = - new nsNSSCertListEnumerator(mCertList.get(), locker); + new nsNSSCertListEnumerator(mCertList, locker); enumerator.forget(_retval); return NS_OK; @@ -1811,8 +1823,10 @@ nsNSSCertList::Equals(nsIX509CertList* other, bool* result) NS_IMPL_ISUPPORTS(nsNSSCertListEnumerator, nsISimpleEnumerator) nsNSSCertListEnumerator::nsNSSCertListEnumerator( - CERTCertList* certList, const nsNSSShutDownPreventionLock& proofOfLock) + const UniqueCERTCertList& certList, + const nsNSSShutDownPreventionLock& proofOfLock) { + MOZ_ASSERT(certList); mCertList = nsNSSCertList::DupCertList(certList, proofOfLock); } @@ -1833,9 +1847,7 @@ void nsNSSCertListEnumerator::virtualDestroyNSSReference() void nsNSSCertListEnumerator::destructorSafeDestroyNSSReference() { - if (mCertList) { - mCertList = nullptr; - } + mCertList = nullptr; } NS_IMETHODIMP diff --git a/security/manager/ssl/nsNSSCertificate.h b/security/manager/ssl/nsNSSCertificate.h index c31c636b34..0cecf05f93 100644 --- a/security/manager/ssl/nsNSSCertificate.h +++ b/security/manager/ssl/nsNSSCertificate.h @@ -3,20 +3,20 @@ * 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 _NS_NSSCERTIFICATE_H_ -#define _NS_NSSCERTIFICATE_H_ +#ifndef nsNSSCertificate_h +#define nsNSSCertificate_h +#include "ScopedNSSTypes.h" +#include "certt.h" +#include "nsCOMPtr.h" +#include "nsIASN1Object.h" +#include "nsIClassInfo.h" +#include "nsISerializable.h" +#include "nsISimpleEnumerator.h" #include "nsIX509Cert.h" #include "nsIX509CertDB.h" #include "nsIX509CertList.h" -#include "nsIASN1Object.h" -#include "nsCOMPtr.h" #include "nsNSSShutDown.h" -#include "nsISimpleEnumerator.h" -#include "nsISerializable.h" -#include "nsIClassInfo.h" -#include "ScopedNSSTypes.h" -#include "certt.h" namespace mozilla { namespace pkix { class DERArray; } } @@ -84,7 +84,7 @@ namespace mozilla { SECStatus ConstructCERTCertListFromReversedDERArray( const mozilla::pkix::DERArray& certArray, - /*out*/ mozilla::ScopedCERTCertList& certList); + /*out*/ mozilla::UniqueCERTCertList& certList); } // namespace mozilla @@ -98,20 +98,21 @@ public: NS_DECL_NSISERIALIZABLE // certList is adopted - nsNSSCertList(mozilla::ScopedCERTCertList& certList, + nsNSSCertList(mozilla::UniqueCERTCertList certList, const nsNSSShutDownPreventionLock& proofOfLock); nsNSSCertList(); - static CERTCertList* DupCertList(CERTCertList* aCertList, - const nsNSSShutDownPreventionLock& - proofOfLock); + static mozilla::UniqueCERTCertList DupCertList( + const mozilla::UniqueCERTCertList& certList, + const nsNSSShutDownPreventionLock& proofOfLock); + private: virtual ~nsNSSCertList(); virtual void virtualDestroyNSSReference() override; void destructorSafeDestroyNSSReference(); - mozilla::ScopedCERTCertList mCertList; + mozilla::UniqueCERTCertList mCertList; nsNSSCertList(const nsNSSCertList&) = delete; void operator=(const nsNSSCertList&) = delete; @@ -124,14 +125,14 @@ public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSISIMPLEENUMERATOR - nsNSSCertListEnumerator(CERTCertList* certList, + nsNSSCertListEnumerator(const mozilla::UniqueCERTCertList& certList, const nsNSSShutDownPreventionLock& proofOfLock); private: virtual ~nsNSSCertListEnumerator(); virtual void virtualDestroyNSSReference() override; void destructorSafeDestroyNSSReference(); - mozilla::ScopedCERTCertList mCertList; + mozilla::UniqueCERTCertList mCertList; nsNSSCertListEnumerator(const nsNSSCertListEnumerator&) = delete; void operator=(const nsNSSCertListEnumerator&) = delete; @@ -144,4 +145,4 @@ private: { 0xbb, 0x20, 0x89, 0x85, 0xa6, 0x32, 0xdf, 0x05 } \ } -#endif // _NS_NSSCERTIFICATE_H_ +#endif // nsNSSCertificate_h diff --git a/security/manager/ssl/nsNSSCertificateDB.cpp b/security/manager/ssl/nsNSSCertificateDB.cpp index ff129c489c..36ce2f2735 100644 --- a/security/manager/ssl/nsNSSCertificateDB.cpp +++ b/security/manager/ssl/nsNSSCertificateDB.cpp @@ -9,6 +9,7 @@ #include "NSSCertDBTrustDomain.h" #include "SharedSSLState.h" #include "mozilla/Base64.h" +#include "mozilla/Casting.h" #include "mozilla/unused.h" #include "nsArray.h" #include "nsArrayUtils.h" @@ -65,14 +66,14 @@ attemptToLogInWithDefaultPassword() // change certificate trust to pass on all platforms. TODO(bug 978120): Do // proper testing and/or implement a better solution so that we are confident // that this does the correct thing outside of xpcshell tests too. - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return MapSECStatus(SECFailure); } - if (PK11_NeedUserInit(slot)) { + if (PK11_NeedUserInit(slot.get())) { // Ignore the return value. Presumably PK11_InitPin will fail if the user // has a non-default password. - (void) PK11_InitPin(slot, nullptr, nullptr); + Unused << PK11_InitPin(slot.get(), nullptr, nullptr); } #endif @@ -179,24 +180,28 @@ nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey, return NS_ERROR_ILLEGAL_INPUT; } const char* reader = decoded.BeginReading(); - uint64_t zeroes = *reinterpret_cast(reader); + uint64_t zeroes = *BitwiseCast(reader); if (zeroes != 0) { return NS_ERROR_ILLEGAL_INPUT; } reader += sizeof(uint64_t); - uint32_t serialNumberLen = ntohl(*reinterpret_cast(reader)); + // Note: We surround the ntohl() argument with parentheses to stop the macro + // from thinking two arguments were passed. + uint32_t serialNumberLen = ntohl( + (*BitwiseCast(reader))); reader += sizeof(uint32_t); - uint32_t issuerLen = ntohl(*reinterpret_cast(reader)); + uint32_t issuerLen = ntohl( + (*BitwiseCast(reader))); reader += sizeof(uint32_t); if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) { return NS_ERROR_ILLEGAL_INPUT; } CERTIssuerAndSN issuerSN; issuerSN.serialNumber.len = serialNumberLen; - issuerSN.serialNumber.data = (unsigned char*)reader; + issuerSN.serialNumber.data = BitwiseCast(reader); reader += serialNumberLen; issuerSN.derIssuer.len = issuerLen; - issuerSN.derIssuer.data = (unsigned char*)reader; + issuerSN.derIssuer.data = BitwiseCast(reader); reader += issuerLen; MOZ_ASSERT(reader == decoded.EndReading()); @@ -237,8 +242,7 @@ nsNSSCertificateDB::getCertsFromPackage(const UniquePLArenaPool& arena, uint8_t* data, uint32_t length, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { - CERTDERCerts* collectArgs = - (CERTDERCerts*)PORT_ArenaZAlloc(arena.get(), sizeof(CERTDERCerts)); + CERTDERCerts* collectArgs = PORT_ArenaZNew(arena.get(), CERTDERCerts); if (!collectArgs) { return nullptr; } @@ -465,9 +469,8 @@ nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length, // Now let's create some certs to work with for (int i = 0; i < certCollection->numcerts; i++) { SECItem* currItem = &certCollection->rawCerts[i]; - nsCOMPtr cert = - nsNSSCertificate::ConstructFromDER(reinterpret_cast(currItem->data), - currItem->len); + nsCOMPtr cert = nsNSSCertificate::ConstructFromDER( + BitwiseCast(currItem->data), currItem->len); if (!cert) { return NS_ERROR_FAILURE; } @@ -554,7 +557,7 @@ ImportCertsIntoTempStorage(int numcerts, SECItem* certs, } static SECStatus -ImportCertsIntoPermanentStorage(const ScopedCERTCertList& certChain, +ImportCertsIntoPermanentStorage(const UniqueCERTCertList& certChain, const SECCertUsage usage, const bool caOnly) { int chainLen = 0; @@ -630,7 +633,7 @@ nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length, continue; } - ScopedCERTCertList certChain; + UniqueCERTCertList certChain; SECStatus srv = certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient, mozilla::pkix::Now(), ctx, @@ -686,7 +689,7 @@ nsNSSCertificateDB::ImportValidCACertsInList(const UniqueCERTCertList& filteredC for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get()); !CERT_LIST_END(node, filteredCerts.get()); node = CERT_LIST_NEXT(node)) { - ScopedCERTCertList certChain; + UniqueCERTCertList certChain; SECStatus rv = certVerifier->VerifyCert(node->cert, certificateUsageVerifyCA, mozilla::pkix::Now(), ctx, @@ -1009,7 +1012,7 @@ nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType) return NS_ERROR_FAILURE; } -NS_IMETHODIMP +NS_IMETHODIMP nsNSSCertificateDB::ImportPKCS12File(nsISupports* aToken, nsIFile* aFile) { nsNSSShutDownPreventionLock locker; @@ -1026,12 +1029,11 @@ nsNSSCertificateDB::ImportPKCS12File(nsISupports* aToken, nsIFile* aFile) return blob.ImportFromFile(aFile); } -NS_IMETHODIMP +NS_IMETHODIMP nsNSSCertificateDB::ExportPKCS12File(nsISupports* aToken, nsIFile* aFile, uint32_t count, nsIX509Cert** certs) - //const char16_t **aCertNames) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { @@ -1043,16 +1045,15 @@ nsNSSCertificateDB::ExportPKCS12File(nsISupports* aToken, if (count == 0) return NS_OK; nsCOMPtr localRef; if (!aToken) { - ScopedPK11SlotInfo keySlot(PK11_GetInternalKeySlot()); - NS_ASSERTION(keySlot,"Failed to get the internal key slot"); - localRef = new nsPK11Token(keySlot); - } - else { + UniquePK11SlotInfo keySlot(PK11_GetInternalKeySlot()); + if (!keySlot) { + return NS_ERROR_FAILURE; + } + localRef = new nsPK11Token(keySlot.get()); + } else { localRef = do_QueryInterface(aToken); } blob.SetToken(localRef); - //blob.LoadCerts(aCertNames, count); - //return blob.ExportToFile(aFile); return blob.ExportToFile(aFile, certs, count); } @@ -1142,7 +1143,7 @@ nsNSSCertificateDB::FindCertByEmailAddress(const char* aEmailAddress, RefPtr certVerifier(GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); - ScopedCERTCertList certlist( + UniqueCERTCertList certlist( PK11_FindCertsFromEmailAddress(aEmailAddress, nullptr)); if (!certlist) return NS_ERROR_FAILURE; @@ -1159,7 +1160,7 @@ nsNSSCertificateDB::FindCertByEmailAddress(const char* aEmailAddress, !CERT_LIST_END(node, certlist); node = CERT_LIST_NEXT(node)) { - ScopedCERTCertList unusedCertChain; + UniqueCERTCertList unusedCertChain; SECStatus srv = certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient, mozilla::pkix::Now(), @@ -1291,13 +1292,9 @@ nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert, NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt); nsAutoCString baseName; - char *temp_nn = PR_smprintf(nickFmt.get(), username.get(), caname.get()); - if (!temp_nn) { + baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get()); + if (baseName.IsEmpty()) { return; - } else { - baseName = temp_nn; - PR_smprintf_free(temp_nn); - temp_nn = nullptr; } nickname = baseName; @@ -1307,49 +1304,43 @@ nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert, * then we need to check for nicknames that already exist on the smart * card. */ - ScopedPK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx)); + UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx)); if (!slot) return; - if (!PK11_IsInternal(slot)) { - char *tmp = PR_smprintf("%s:%s", PK11_GetTokenName(slot), baseName.get()); - if (!tmp) { + if (!PK11_IsInternal(slot.get())) { + nsAutoCString tmp; + tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get()); + if (tmp.IsEmpty()) { nickname.Truncate(); return; } baseName = tmp; - PR_smprintf_free(tmp); - nickname = baseName; } int count = 1; while (true) { if ( count > 1 ) { - char *tmp = PR_smprintf("%s #%d", baseName.get(), count); - if (!tmp) { + nsAutoCString tmp; + tmp.AppendPrintf("%s #%d", baseName.get(), count); + if (tmp.IsEmpty()) { nickname.Truncate(); return; } nickname = tmp; - PR_smprintf_free(tmp); } UniqueCERTCertificate dummycert; - if (PK11_IsInternal(slot)) { + if (PK11_IsInternal(slot.get())) { /* look up the nickname to make sure it isn't in use already */ dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get())); } else { - /* - * Check the cert against others that already live on the smart - * card. - */ + // Check the cert against others that already live on the smart card. dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx)); if (dummycert) { - /* - * Make sure the subject names are different. - */ + // Make sure the subject names are different. if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) { /* @@ -1471,15 +1462,15 @@ nsNSSCertificateDB::GetCerts(nsIX509CertList **_retval) nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; - } + } nsCOMPtr ctx = new PipUIContext(); nsCOMPtr nssCertList; - ScopedCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx)); + UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx)); // nsNSSCertList 1) adopts certList, and 2) handles the nullptr case fine. // (returns an empty list) - nssCertList = new nsNSSCertList(certList, locker); + nssCertList = new nsNSSCertList(Move(certList), locker); nssCertList.forget(_retval); return NS_OK; @@ -1517,7 +1508,7 @@ VerifyCertAtTime(nsIX509Cert* aCert, RefPtr certVerifier(GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE); - ScopedCERTCertList resultChain; + UniqueCERTCertList resultChain; SECOidTag evOidPolicy; SECStatus srv; @@ -1545,7 +1536,7 @@ VerifyCertAtTime(nsIX509Cert* aCert, nsCOMPtr nssCertList; // This adopts the list - nssCertList = new nsNSSCertList(resultChain, locker); + nssCertList = new nsNSSCertList(Move(resultChain), locker); NS_ENSURE_TRUE(nssCertList, NS_ERROR_FAILURE); if (srv == SECSuccess) { diff --git a/security/manager/ssl/nsNSSCertificateDB.h b/security/manager/ssl/nsNSSCertificateDB.h index 4258a153bf..23502460a3 100644 --- a/security/manager/ssl/nsNSSCertificateDB.h +++ b/security/manager/ssl/nsNSSCertificateDB.h @@ -5,13 +5,14 @@ #ifndef nsNSSCertificateDB_h #define nsNSSCertificateDB_h +#include "ScopedNSSTypes.h" #include "certt.h" #include "mozilla/Mutex.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" #include "nsIX509CertDB.h" #include "nsNSSShutDown.h" -#include "ScopedNSSTypes.h" +#include "nsString.h" class nsCString; class nsIArray; diff --git a/security/manager/ssl/nsNSSCertificateFakeTransport.cpp b/security/manager/ssl/nsNSSCertificateFakeTransport.cpp index 81863c7c6e..45f0f9d6d8 100644 --- a/security/manager/ssl/nsNSSCertificateFakeTransport.cpp +++ b/security/manager/ssl/nsNSSCertificateFakeTransport.cpp @@ -425,7 +425,7 @@ nsNSSCertListFakeTransport::DeleteCert(nsIX509Cert* aCert) return NS_ERROR_NOT_IMPLEMENTED; } -void* +CERTCertList* nsNSSCertListFakeTransport::GetRawCertList() { NS_NOTREACHED("Unimplemented on content process"); diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index a0923d9863..2ceabd8829 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -12,6 +12,7 @@ #include "SharedSSLState.h" #include "cert.h" #include "certdb.h" +#include "mozilla/Casting.h" #include "mozilla/Preferences.h" #include "mozilla/PublicSSL.h" #include "mozilla/Services.h" @@ -440,7 +441,7 @@ GetUserSid(nsAString& sidString) return false; } char sid_buffer[SECURITY_MAX_SID_SIZE]; - SID* sid = reinterpret_cast(sid_buffer); + SID* sid = BitwiseCast(sid_buffer); DWORD cbSid = MOZ_ARRAY_LENGTH(sid_buffer); SID_NAME_USE eUse; // There doesn't appear to be a defined maximum length for the domain name diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index 3240ab21a7..0e2f68e337 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -413,7 +413,7 @@ nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval) } nsAutoCString hostnameFlat(PromiseFlatCString(hostname)); CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY; - ScopedCERTCertList unusedBuiltChain; + UniqueCERTCertList unusedBuiltChain; SECStatus rv = certVerifier->VerifySSLServerCert(nssCert, nullptr, mozilla::pkix::Now(), nullptr, hostnameFlat.get(), @@ -505,7 +505,7 @@ nsNSSSocketInfo::SetNPNList(nsTArray& protocolArray) if (SSL_SetNextProtoNego( mFd, - reinterpret_cast(npnList.get()), + BitwiseCast(npnList.get()), npnList.Length()) != SECSuccess) return NS_ERROR_FAILURE; @@ -2005,7 +2005,7 @@ nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket, } RefPtr info( - reinterpret_cast(socket->higher->secret)); + BitwiseCast(socket->higher->secret)); UniqueCERTCertificate serverCert(SSL_PeerCertificate(socket)); if (!serverCert) { @@ -2057,7 +2057,7 @@ ClientAuthDataRunnable::RunOnTargetThread() char** caNameStrings; UniqueCERTCertificate cert; UniqueSECKEYPrivateKey privKey; - ScopedCERTCertList certList; + UniqueCERTCertList certList; CERTCertListNode* node; UniqueCERTCertNicknames nicknames; int keyError = 0; // used for private key retrieval error @@ -2110,9 +2110,9 @@ ClientAuthDataRunnable::RunOnTargetThread() // automatically find the right cert // find all user certs that are valid and for SSL - certList = CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), - certUsageSSLClient, false, - true, wincx); + certList.reset(CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), + certUsageSSLClient, false, true, + wincx)); if (!certList) { goto noCert; } @@ -2202,7 +2202,7 @@ ClientAuthDataRunnable::RunOnTargetThread() getter_AddRefs(found_cert)); if (NS_SUCCEEDED(find_rv) && found_cert) { nsNSSCertificate* obj_cert = - reinterpret_cast(found_cert.get()); + BitwiseCast(found_cert.get()); if (obj_cert) { cert.reset(obj_cert->GetCert()); } @@ -2224,9 +2224,9 @@ ClientAuthDataRunnable::RunOnTargetThread() // find all user certs that are for SSL // note that we are allowing expired certs in this list - certList = CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), - certUsageSSLClient, false, - false, wincx); + certList.reset(CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), + certUsageSSLClient, false, + false, wincx)); if (!certList) { goto noCert; } @@ -2257,7 +2257,7 @@ ClientAuthDataRunnable::RunOnTargetThread() goto noCert; } - nicknames.reset(getNSSCertNicknamesFromCertList(certList.get())); + nicknames.reset(getNSSCertNicknamesFromCertList(certList)); if (!nicknames) { goto loser; diff --git a/security/manager/ssl/nsNSSShutDown.cpp b/security/manager/ssl/nsNSSShutDown.cpp index 27f8d0bc12..adcbadcef1 100644 --- a/security/manager/ssl/nsNSSShutDown.cpp +++ b/security/manager/ssl/nsNSSShutDown.cpp @@ -2,6 +2,7 @@ * 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 "mozilla/Casting.h" #include "nsNSSShutDown.h" #include "nsCOMPtr.h" @@ -114,8 +115,8 @@ nsresult nsNSSShutDownList::doPK11Logout() !iter.Done(); iter.Next()) { auto entry = static_cast(iter.Get()); - nsOnPK11LogoutCancelObject *pklco = - reinterpret_cast(entry->obj); + nsOnPK11LogoutCancelObject* pklco = + BitwiseCast(entry->obj); if (pklco) { pklco->logout(); } diff --git a/security/manager/ssl/nsNSSU2FToken.cpp b/security/manager/ssl/nsNSSU2FToken.cpp index 63c1eaf85d..7fd5d36ae0 100644 --- a/security/manager/ssl/nsNSSU2FToken.cpp +++ b/security/manager/ssl/nsNSSU2FToken.cpp @@ -7,6 +7,7 @@ #include "nsNSSU2FToken.h" #include "CryptoBuffer.h" +#include "mozilla/Casting.h" #include "nsNSSComponent.h" #include "pk11pub.h" #include "prerror.h" @@ -83,7 +84,7 @@ nsNSSU2FToken::destructorSafeDestroyNSSReference() } static PK11SymKey* -GetSymKeyByNickname(PK11SlotInfo* aSlot, +GetSymKeyByNickname(const UniquePK11SlotInfo& aSlot, nsCString aNickname, const nsNSSShutDownPreventionLock&) { @@ -91,13 +92,13 @@ GetSymKeyByNickname(PK11SlotInfo* aSlot, ("Searching for a symmetric key named %s", aNickname.get())); PK11SymKey* keyList; - keyList = PK11_ListFixedKeysInSlot(aSlot, const_cast(aNickname.get()), + keyList = PK11_ListFixedKeysInSlot(aSlot.get(), + const_cast(aNickname.get()), /* wincx */ nullptr); while (keyList) { ScopedPK11SymKey freeKey(keyList); - UniquePtr - freeKeyName(PK11_GetSymKeyNickname(freeKey), PORT_Free); + UniquePORTString freeKeyName(PK11_GetSymKeyNickname(freeKey.get())); if (aNickname == freeKeyName.get()) { MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key found!")); @@ -112,7 +113,7 @@ GetSymKeyByNickname(PK11SlotInfo* aSlot, } static nsresult -GenEcKeypair(PK11SlotInfo* aSlot, ScopedSECKEYPrivateKey& aPrivKey, +GenEcKeypair(const UniquePK11SlotInfo& aSlot, ScopedSECKEYPrivateKey& aPrivKey, ScopedSECKEYPublicKey& aPubKey, const nsNSSShutDownPreventionLock&) { UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); @@ -130,7 +131,7 @@ GenEcKeypair(PK11SlotInfo* aSlot, ScopedSECKEYPrivateKey& aPrivKey, CK_MECHANISM_TYPE mechanism = CKM_EC_KEY_PAIR_GEN; SECKEYPublicKey* pubKeyRaw; - aPrivKey = PK11_GenerateKeyPair(aSlot, mechanism, keyParams, &pubKeyRaw, + aPrivKey = PK11_GenerateKeyPair(aSlot.get(), mechanism, keyParams, &pubKeyRaw, /* ephemeral */ PR_FALSE, PR_FALSE, /* wincx */ nullptr); aPubKey = pubKeyRaw; @@ -147,7 +148,7 @@ GenEcKeypair(PK11SlotInfo* aSlot, ScopedSECKEYPrivateKey& aPrivKey, } nsresult -nsNSSU2FToken::GetOrCreateWrappingKey(PK11SlotInfo* aSlot, +nsNSSU2FToken::GetOrCreateWrappingKey(const UniquePK11SlotInfo& aSlot, const nsNSSShutDownPreventionLock& locker) { // Search for an existing wrapping key. If we find it, @@ -164,7 +165,7 @@ nsNSSU2FToken::GetOrCreateWrappingKey(PK11SlotInfo* aSlot, // We did not find an existing wrapping key, so we generate one in the // persistent database (e.g, Token). - mWrappingKey = PK11_TokenKeyGenWithFlags(aSlot, CKM_AES_KEY_GEN, + mWrappingKey = PK11_TokenKeyGenWithFlags(aSlot.get(), CKM_AES_KEY_GEN, /* default params */ nullptr, kWrappingKeyByteLen, /* empty keyid */ nullptr, @@ -194,7 +195,7 @@ nsNSSU2FToken::GetOrCreateWrappingKey(PK11SlotInfo* aSlot, } static nsresult -GetAttestationCertificate(PK11SlotInfo* aSlot, +GetAttestationCertificate(const UniquePK11SlotInfo& aSlot, ScopedSECKEYPrivateKey& aAttestPrivKey, ScopedCERTCertificate& aAttestCert, const nsNSSShutDownPreventionLock& locker) @@ -245,8 +246,10 @@ GetAttestationCertificate(PK11SlotInfo* aSlot, } unsigned long serial; - unsigned char* serialBytes = reinterpret_cast(&serial); - SECStatus srv = PK11_GenerateRandomOnSlot(aSlot, serialBytes, sizeof(serial)); + unsigned char* serialBytes = + mozilla::BitwiseCast(&serial); + SECStatus srv = PK11_GenerateRandomOnSlot(aSlot.get(), serialBytes, + sizeof(serial)); if (srv != SECSuccess) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to gen serial, NSS error #%d", PORT_GetError())); @@ -320,11 +323,11 @@ nsNSSU2FToken::Init() return NS_ERROR_NOT_AVAILABLE; } - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); MOZ_ASSERT(slot.get()); // Search for an existing wrapping key, or create one. - nsresult rv = GetOrCreateWrappingKey(slot.get(), locker); + nsresult rv = GetOrCreateWrappingKey(slot, locker); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -337,7 +340,7 @@ nsNSSU2FToken::Init() // Convert a Private Key object into an opaque key handle, using AES Key Wrap // and aWrappingKey to convert aPrivKey. static SECItem* -KeyHandleFromPrivateKey(PK11SlotInfo* aSlot, +KeyHandleFromPrivateKey(const UniquePK11SlotInfo& aSlot, PK11SymKey* aWrappingKey, SECKEYPrivateKey* aPrivKey, const nsNSSShutDownPreventionLock&) @@ -354,7 +357,7 @@ KeyHandleFromPrivateKey(PK11SlotInfo* aSlot, ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, /* default IV */ nullptr )); - SECStatus srv = PK11_WrapPrivKey(aSlot, aWrappingKey, aPrivKey, + SECStatus srv = PK11_WrapPrivKey(aSlot.get(), aWrappingKey, aPrivKey, CKM_NSS_AES_KEY_WRAP_PAD, param, wrappedKey.get(), /* wincx */ nullptr); if (srv != SECSuccess) { @@ -369,7 +372,8 @@ KeyHandleFromPrivateKey(PK11SlotInfo* aSlot, // Convert an opaque key handle aKeyHandle back into a Private Key object, using // aWrappingKey and the AES Key Wrap algorithm. static SECKEYPrivateKey* -PrivateKeyFromKeyHandle(PK11SlotInfo* aSlot, PK11SymKey* aWrappingKey, +PrivateKeyFromKeyHandle(const UniquePK11SlotInfo& aSlot, + PK11SymKey* aWrappingKey, uint8_t* aKeyHandle, uint32_t aKeyHandleLen, const nsNSSShutDownPreventionLock&) { @@ -385,7 +389,7 @@ PrivateKeyFromKeyHandle(PK11SlotInfo* aSlot, PK11SymKey* aWrappingKey, int usageCount = 1; SECKEYPrivateKey* unwrappedKey; - unwrappedKey = PK11_UnwrapPrivKey(aSlot, aWrappingKey, + unwrappedKey = PK11_UnwrapPrivKey(aSlot.get(), aWrappingKey, CKM_NSS_AES_KEY_WRAP_PAD, param, &keyHandleItem, /* no nickname */ nullptr, @@ -437,11 +441,11 @@ nsNSSU2FToken::IsRegistered(uint8_t* aKeyHandle, uint32_t aKeyHandleLen, return NS_ERROR_FAILURE; } - ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); // Decode the key handle - ScopedSECKEYPrivateKey privKey(PrivateKeyFromKeyHandle(slot.get(), + UniqueSECKEYPrivateKey privKey(PrivateKeyFromKeyHandle(slot, mWrappingKey.get(), aKeyHandle, aKeyHandleLen, @@ -500,13 +504,13 @@ nsNSSU2FToken::Register(uint8_t* aApplication, // We should already have a wrapping key MOZ_ASSERT(mWrappingKey); - ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); // Construct a one-time-use Attestation Certificate ScopedSECKEYPrivateKey attestPrivKey; ScopedCERTCertificate attestCert; - nsresult rv = GetAttestationCertificate(slot.get(), attestPrivKey, attestCert, + nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert, locker); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; @@ -517,14 +521,13 @@ nsNSSU2FToken::Register(uint8_t* aApplication, // Generate a new keypair; the private will be wrapped into a Key Handle ScopedSECKEYPrivateKey privKey; ScopedSECKEYPublicKey pubKey; - rv = GenEcKeypair(slot.get(), privKey, pubKey, locker); + rv = GenEcKeypair(slot, privKey, pubKey, locker); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } // The key handle will be the result of keywrap(privKey, key=mWrappingKey) - ScopedSECItem keyHandleItem(KeyHandleFromPrivateKey(slot.get(), - mWrappingKey.get(), + UniqueSECItem keyHandleItem(KeyHandleFromPrivateKey(slot, mWrappingKey.get(), privKey.get(), locker)); if (!keyHandleItem.get()) { @@ -628,7 +631,7 @@ nsNSSU2FToken::Sign(uint8_t* aApplication, uint32_t aApplicationLen, MOZ_ASSERT(mWrappingKey); - ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); if ((aChallengeLen != kParamLen) || (aApplicationLen != kParamLen)) { @@ -640,7 +643,7 @@ nsNSSU2FToken::Sign(uint8_t* aApplication, uint32_t aApplicationLen, } // Decode the key handle - ScopedSECKEYPrivateKey privKey(PrivateKeyFromKeyHandle(slot.get(), + UniqueSECKEYPrivateKey privKey(PrivateKeyFromKeyHandle(slot, mWrappingKey.get(), aKeyHandle, aKeyHandleLen, diff --git a/security/manager/ssl/nsNSSU2FToken.h b/security/manager/ssl/nsNSSU2FToken.h index d725b0da10..814aa99fc3 100644 --- a/security/manager/ssl/nsNSSU2FToken.h +++ b/security/manager/ssl/nsNSSU2FToken.h @@ -36,7 +36,7 @@ private: static const nsString mVersion; ~nsNSSU2FToken(); - nsresult GetOrCreateWrappingKey(PK11SlotInfo* aSlot, + nsresult GetOrCreateWrappingKey(const mozilla::UniquePK11SlotInfo& aSlot, const nsNSSShutDownPreventionLock&); }; diff --git a/security/manager/ssl/nsNTLMAuthModule.cpp b/security/manager/ssl/nsNTLMAuthModule.cpp index 8ef2223711..e095a51af9 100644 --- a/security/manager/ssl/nsNTLMAuthModule.cpp +++ b/security/manager/ssl/nsNTLMAuthModule.cpp @@ -7,7 +7,9 @@ #include +#include "ScopedNSSTypes.h" #include "md4.h" +#include "mozilla/Casting.h" #include "mozilla/CheckedInt.h" #include "mozilla/Endian.h" #include "mozilla/Likely.h" @@ -15,16 +17,15 @@ #include "mozilla/Snprintf.h" #include "nsCOMPtr.h" #include "nsComponentManagerUtils.h" -#include "nsICryptoHash.h" #include "nsICryptoHMAC.h" +#include "nsICryptoHash.h" #include "nsIKeyModule.h" #include "nsKeyModule.h" +#include "nsNSSShutDown.h" #include "nsNativeCharsetUtils.h" #include "nsNetCID.h" -#include "nsNSSShutDown.h" #include "nsUnicharUtils.h" #include "pk11pub.h" -#include "mozilla/Logging.h" #include "prsystem.h" static bool sNTLMv1Forced = false; @@ -449,7 +450,7 @@ ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg) if (inLen < NTLM_TYPE2_HEADER_LEN) return NS_ERROR_UNEXPECTED; - const uint8_t *cursor = reinterpret_cast(inBuf); + auto cursor = static_cast(inBuf); // verify NTLMSSP signature if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0) @@ -476,7 +477,7 @@ ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg) // integer overflow checking. if (MOZ_LIKELY(targetEnd.isValid() && targetEnd.value() <= inLen)) { msg->targetLen = targetLen; - msg->target = reinterpret_cast(inBuf) + offset; + msg->target = static_cast(inBuf) + offset; } else { // Do not error out, for (conservative) backward compatibility. msg->targetLen = 0; @@ -491,8 +492,9 @@ ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg) cursor += sizeof(msg->challenge); LOG(("NTLM type 2 message:\n")); - LogBuf("target", reinterpret_cast (msg->target), msg->targetLen); - LogBuf("flags", reinterpret_cast (&msg->flags), 4); + LogBuf("target", msg->target, msg->targetLen); + LogBuf("flags", + mozilla::BitwiseCast(&msg->flags), 4); LogFlags(msg->flags); LogBuf("challenge", msg->challenge, sizeof(msg->challenge)); @@ -512,7 +514,7 @@ ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg) // integer overflow checking. if (MOZ_LIKELY(targetInfoEnd.isValid() && targetInfoEnd.value() <= inLen)) { msg->targetInfoLen = targetInfoLen; - msg->targetInfo = reinterpret_cast(inBuf) + offset; + msg->targetInfo = static_cast(inBuf) + offset; } else { NS_ERROR("failed to get NTLMv2 target info"); return NS_ERROR_UNEXPECTED; @@ -570,7 +572,8 @@ GenerateType3Msg(const nsString &domain, ucsDomainBuf = domain; domainPtr = ucsDomainBuf.get(); domainLen = ucsDomainBuf.Length() * 2; - WriteUnicodeLE((void *) domainPtr, reinterpret_cast (domainPtr), + WriteUnicodeLE(const_cast(domainPtr), + static_cast(domainPtr), ucsDomainBuf.Length()); #else domainPtr = domain.get(); @@ -593,7 +596,8 @@ GenerateType3Msg(const nsString &domain, ucsUserBuf = username; userPtr = ucsUserBuf.get(); userLen = ucsUserBuf.Length() * 2; - WriteUnicodeLE((void *) userPtr, reinterpret_cast (userPtr), + WriteUnicodeLE(const_cast(userPtr), + static_cast(userPtr), ucsUserBuf.Length()); #else userPtr = username.get(); @@ -623,7 +627,8 @@ GenerateType3Msg(const nsString &domain, hostPtr = ucsHostBuf.get(); hostLen = ucsHostBuf.Length() * 2; #ifdef IS_BIG_ENDIAN - WriteUnicodeLE((void *) hostPtr, reinterpret_cast (hostPtr), + WriteUnicodeLE(const_cast(hostPtr), + static_cast(hostPtr), ucsHostBuf.Length()); #endif } @@ -669,19 +674,22 @@ GenerateType3Msg(const nsString &domain, userUpperPtr = ucsUserUpperBuf.get(); userUpperLen = ucsUserUpperBuf.Length() * 2; #ifdef IS_BIG_ENDIAN - WriteUnicodeLE((void *) userUpperPtr, reinterpret_cast (userUpperPtr), + WriteUnicodeLE(const_cast(userUpperPtr), + static_cast(userUpperPtr), ucsUserUpperBuf.Length()); #endif ToUpperCase(domain, ucsDomainUpperBuf); domainUpperPtr = ucsDomainUpperBuf.get(); domainUpperLen = ucsDomainUpperBuf.Length() * 2; #ifdef IS_BIG_ENDIAN - WriteUnicodeLE((void *) domainUpperPtr, reinterpret_cast (domainUpperPtr), + WriteUnicodeLE(const_cast(domainUpperPtr), + static_cast(domainUpperPtr), ucsDomainUpperBuf.Length()); #endif NTLM_Hash(password, ntlmHash); - ntlmHashStr = nsAutoCString(reinterpret_cast(ntlmHash), NTLM_HASH_LEN); + ntlmHashStr = nsAutoCString( + mozilla::BitwiseCast(ntlmHash), NTLM_HASH_LEN); nsCOMPtr keyFactory = do_CreateInstance(NS_KEYMODULEOBJECTFACTORY_CONTRACTID, &rv); @@ -710,11 +718,12 @@ GenerateType3Msg(const nsString &domain, if (NS_FAILED(rv)) { return rv; } - rv = hasher->Update(reinterpret_cast (userUpperPtr), userUpperLen); + rv = hasher->Update(static_cast(userUpperPtr), userUpperLen); if (NS_FAILED(rv)) { return rv; } - rv = hasher->Update(reinterpret_cast (domainUpperPtr), domainUpperLen); + rv = hasher->Update(static_cast(domainUpperPtr), + domainUpperLen); if (NS_FAILED(rv)) { return rv; } @@ -786,7 +795,7 @@ GenerateType3Msg(const nsString &domain, if (NS_FAILED(rv)) { return rv; } - rv = hasher->Update(reinterpret_cast (msg.targetInfo), msg.targetInfoLen); + rv = hasher->Update(msg.targetInfo, msg.targetInfoLen); if (NS_FAILED(rv)) { return rv; } @@ -809,7 +818,6 @@ GenerateType3Msg(const nsString &domain, } else if (msg.flags & NTLM_NegotiateNTLM2Key) { // compute NTLM2 session response nsCString sessionHashString; - const uint8_t *sessionHash; PK11_GenerateRandom(lmResp, NTLM_CHAL_LEN); memset(lmResp + NTLM_CHAL_LEN, 0, LM_RESP_LEN - NTLM_CHAL_LEN); @@ -836,7 +844,8 @@ GenerateType3Msg(const nsString &domain, return rv; } - sessionHash = reinterpret_cast (sessionHashString.get()); + auto sessionHash = mozilla::BitwiseCast( + sessionHashString.get()); LogBuf("NTLM2 effective key: ", sessionHash, 8); @@ -890,7 +899,7 @@ GenerateType3Msg(const nsString &domain, return NS_ERROR_UNEXPECTED; } cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset.value()); - memcpy((uint8_t *) *outBuf + offset.value(), lmResp, LM_RESP_LEN); + memcpy(static_cast(*outBuf) + offset.value(), lmResp, LM_RESP_LEN); // 20 : NTLM or NTLMv2 response sec buf offset += LM_RESP_LEN; @@ -900,26 +909,30 @@ GenerateType3Msg(const nsString &domain, } cursor = WriteSecBuf(cursor, ntlmRespLen.value(), offset.value()); if (ntlmv2) { - memcpy(reinterpret_cast (*outBuf) + offset.value(), ntlmv2Resp, NTLMv2_RESP_LEN); + memcpy(static_cast(*outBuf) + offset.value(), ntlmv2Resp, + NTLMv2_RESP_LEN); offset += NTLMv2_RESP_LEN; if (!offset.isValid()) { NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); return NS_ERROR_UNEXPECTED; } - memcpy(reinterpret_cast (*outBuf) + offset.value(), ntlmv2_blob1, NTLMv2_BLOB1_LEN); + memcpy(static_cast(*outBuf) + offset.value(), ntlmv2_blob1, + NTLMv2_BLOB1_LEN); offset += NTLMv2_BLOB1_LEN; if (!offset.isValid()) { NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); return NS_ERROR_UNEXPECTED; } - memcpy(reinterpret_cast (*outBuf) + offset.value(), msg.targetInfo, msg.targetInfoLen); + memcpy(static_cast(*outBuf) + offset.value(), msg.targetInfo, + msg.targetInfoLen); } else { - memcpy(reinterpret_cast (*outBuf) + offset.value(), ntlmResp, NTLM_RESP_LEN); + memcpy(static_cast(*outBuf) + offset.value(), ntlmResp, + NTLM_RESP_LEN); } // 28 : domain name sec buf offset = NTLM_TYPE3_HEADER_LEN; cursor = WriteSecBuf(cursor, domainLen, offset.value()); - memcpy((uint8_t *) *outBuf + offset.value(), domainPtr, domainLen); + memcpy(static_cast(*outBuf) + offset.value(), domainPtr, domainLen); // 36 : user name sec buf offset += domainLen; @@ -928,7 +941,7 @@ GenerateType3Msg(const nsString &domain, return NS_ERROR_UNEXPECTED; } cursor = WriteSecBuf(cursor, userLen, offset.value()); - memcpy(reinterpret_cast (*outBuf) + offset.value(), userPtr, userLen); + memcpy(static_cast(*outBuf) + offset.value(), userPtr, userLen); // 44 : workstation (host) name sec buf offset += userLen; @@ -937,7 +950,7 @@ GenerateType3Msg(const nsString &domain, return NS_ERROR_UNEXPECTED; } cursor = WriteSecBuf(cursor, hostLen, offset.value()); - memcpy(reinterpret_cast (*outBuf) + offset.value(), hostPtr, hostLen); + memcpy(static_cast(*outBuf) + offset.value(), hostPtr, hostLen); // 52 : session key sec buf (not used) cursor = WriteSecBuf(cursor, 0, 0); @@ -1091,14 +1104,13 @@ static void des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash) { CK_MECHANISM_TYPE cipherMech = CKM_DES_ECB; - PK11SlotInfo *slot = nullptr; PK11SymKey *symkey = nullptr; PK11Context *ctxt = nullptr; SECItem keyItem, *param = nullptr; SECStatus rv; unsigned int n; - - slot = PK11_GetBestSlot(cipherMech, nullptr); + + mozilla::UniquePK11SlotInfo slot(PK11_GetBestSlot(cipherMech, nullptr)); if (!slot) { NS_ERROR("no slot"); @@ -1107,7 +1119,7 @@ des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash) keyItem.data = (uint8_t *) key; keyItem.len = 8; - symkey = PK11_ImportSymKey(slot, cipherMech, + symkey = PK11_ImportSymKey(slot.get(), cipherMech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr); if (!symkey) @@ -1150,6 +1162,4 @@ done: PK11_FreeSymKey(symkey); if (param) SECITEM_FreeItem(param, true); - if (slot) - PK11_FreeSlot(slot); } diff --git a/security/manager/ssl/nsPK11TokenDB.cpp b/security/manager/ssl/nsPK11TokenDB.cpp index 1190d3ee62..061682804c 100644 --- a/security/manager/ssl/nsPK11TokenDB.cpp +++ b/security/manager/ssl/nsPK11TokenDB.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsPK11TokenDB.h" +#include "mozilla/Casting.h" #include "mozilla/unused.h" #include "nsIMutableArray.h" #include "nsISupports.h" @@ -46,7 +47,7 @@ nsPK11Token::refreshTokenInfo(const nsNSSShutDownPreventionLock& /*proofOfLock*/ } // Set the Label field - const char* ccLabel = reinterpret_cast(tokInfo.label); + const char* ccLabel = mozilla::BitwiseCast(tokInfo.label); const nsACString& cLabel = Substring( ccLabel, ccLabel + PL_strnlen(ccLabel, sizeof(tokInfo.label))); @@ -54,7 +55,8 @@ nsPK11Token::refreshTokenInfo(const nsNSSShutDownPreventionLock& /*proofOfLock*/ mTokenLabel.Trim(" ", false, true); // Set the Manufacturer field - const char* ccManID = reinterpret_cast(tokInfo.manufacturerID); + const char* ccManID = + mozilla::BitwiseCast(tokInfo.manufacturerID); const nsACString& cManID = Substring( ccManID, ccManID + PL_strnlen(ccManID, sizeof(tokInfo.manufacturerID))); @@ -72,7 +74,8 @@ nsPK11Token::refreshTokenInfo(const nsNSSShutDownPreventionLock& /*proofOfLock*/ mTokenFWVersion.AppendInt(tokInfo.firmwareVersion.minor); // Set the Serial Number field - const char* ccSerial = reinterpret_cast(tokInfo.serialNumber); + const char* ccSerial = + mozilla::BitwiseCast(tokInfo.serialNumber); const nsACString& cSerial = Substring( ccSerial, ccSerial + PL_strnlen(ccSerial, sizeof(tokInfo.serialNumber))); diff --git a/security/manager/ssl/nsPKCS11Slot.cpp b/security/manager/ssl/nsPKCS11Slot.cpp index a0da2c2239..bced1531f2 100644 --- a/security/manager/ssl/nsPKCS11Slot.cpp +++ b/security/manager/ssl/nsPKCS11Slot.cpp @@ -4,6 +4,7 @@ #include "nsPKCS11Slot.h" +#include "mozilla/Casting.h" #include "mozilla/Logging.h" #include "mozilla/Telemetry.h" #include "mozilla/unused.h" @@ -41,7 +42,8 @@ nsPKCS11Slot::refreshSlotInfo(const nsNSSShutDownPreventionLock& /*proofOfLock*/ } // Set the Description field - const char* ccDesc = reinterpret_cast(slotInfo.slotDescription); + const char* ccDesc = + mozilla::BitwiseCast(slotInfo.slotDescription); const nsACString& cDesc = Substring( ccDesc, ccDesc + PL_strnlen(ccDesc, sizeof(slotInfo.slotDescription))); @@ -49,7 +51,8 @@ nsPKCS11Slot::refreshSlotInfo(const nsNSSShutDownPreventionLock& /*proofOfLock*/ mSlotDesc.Trim(" ", false, true); // Set the Manufacturer field - const char* ccManID = reinterpret_cast(slotInfo.manufacturerID); + const char* ccManID = + mozilla::BitwiseCast(slotInfo.manufacturerID); const nsACString& cManID = Substring( ccManID, ccManID + PL_strnlen(ccManID, sizeof(slotInfo.manufacturerID))); diff --git a/security/manager/ssl/nsPKCS12Blob.cpp b/security/manager/ssl/nsPKCS12Blob.cpp index f856e20866..3273c35ea1 100644 --- a/security/manager/ssl/nsPKCS12Blob.cpp +++ b/security/manager/ssl/nsPKCS12Blob.cpp @@ -5,7 +5,9 @@ #include "nsPKCS12Blob.h" #include "ScopedNSSTypes.h" +#include "mozilla/Casting.h" #include "nsCRT.h" +#include "nsCRTGlue.h" #include "nsDirectoryServiceDefs.h" #include "nsICertificateDialogs.h" #include "nsIDirectoryService.h" @@ -15,7 +17,6 @@ #include "nsNSSCertificate.h" #include "nsNSSComponent.h" #include "nsNSSHelper.h" -#include "nsNSSHelper.h" #include "nsNSSShutDown.h" #include "nsNetUtil.h" #include "nsPK11TokenDB.h" @@ -146,10 +147,10 @@ nsPKCS12Blob::ImportFromFileHelper(nsIFile *file, SEC_PKCS12DecoderContext *dcx = nullptr; SECItem unicodePw; - PK11SlotInfo *slot=nullptr; + UniquePK11SlotInfo slot; nsXPIDLString tokenName; unicodePw.data = nullptr; - + aWantRetry = rr_do_not_retry; if (aImportMode == im_try_zero_length_secitem) @@ -166,11 +167,11 @@ nsPKCS12Blob::ImportFromFileHelper(nsIFile *file, return NS_OK; } } - + mToken->GetTokenName(getter_Copies(tokenName)); { NS_ConvertUTF16toUTF8 tokenNameCString(tokenName); - slot = PK11_FindSlotByName(tokenNameCString.get()); + slot = UniquePK11SlotInfo(PK11_FindSlotByName(tokenNameCString.get())); } if (!slot) { srv = SECFailure; @@ -178,7 +179,7 @@ nsPKCS12Blob::ImportFromFileHelper(nsIFile *file, } // initialize the decoder - dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, nullptr, + dcx = SEC_PKCS12DecoderStart(&unicodePw, slot.get(), nullptr, digest_open, digest_close, digest_read, digest_write, this); @@ -228,11 +229,9 @@ finish: { handleError(PIP_PKCS12_NSS_ERROR); } - } else if (NS_FAILED(rv)) { + } else if (NS_FAILED(rv)) { handleError(PIP_PKCS12_RESTORE_FAILED); } - if (slot) - PK11_FreeSlot(slot); // finish the decoder if (dcx) SEC_PKCS12DecoderFinish(dcx); @@ -413,22 +412,17 @@ finish: // // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to // a buffer of octets. Must handle byte order correctly. -// TODO: Is there a mozilla way to do this? In the string lib? -void +nsresult nsPKCS12Blob::unicodeToItem(const char16_t *uni, SECItem *item) { - int len = 0; - while (uni[len++] != 0); - SECITEM_AllocItem(nullptr, item, sizeof(char16_t) * len); -#ifdef IS_LITTLE_ENDIAN - int i = 0; - for (i=0; idata[2*i ] = (unsigned char )(uni[i] << 8); - item->data[2*i+1] = (unsigned char )(uni[i]); + uint32_t len = NS_strlen(uni) + 1; + if (!SECITEM_AllocItem(nullptr, item, sizeof(char16_t) * len)) { + return NS_ERROR_OUT_OF_MEMORY; } -#else - memcpy(item->data, uni, item->len); -#endif + + mozilla::NativeEndian::copyAndSwapToBigEndian(item->data, uni, len); + + return NS_OK; } // newPKCS12FilePassword @@ -448,8 +442,7 @@ nsPKCS12Blob::newPKCS12FilePassword(SECItem *unicodePw) bool pressedOK; rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK); if (NS_FAILED(rv) || !pressedOK) return rv; - unicodeToItem(password.get(), unicodePw); - return NS_OK; + return unicodeToItem(password.get(), unicodePw); } // getPKCS12FilePassword @@ -469,8 +462,7 @@ nsPKCS12Blob::getPKCS12FilePassword(SECItem *unicodePw) bool pressedOK; rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK); if (NS_FAILED(rv) || !pressedOK) return rv; - unicodeToItem(password.get(), unicodePw); - return NS_OK; + return unicodeToItem(password.get(), unicodePw); } // inputToDecoder @@ -522,9 +514,9 @@ nsPKCS12Blob::inputToDecoder(SEC_PKCS12DecoderContext *dcx, nsIFile *file) SECStatus nsPKCS12Blob::digest_open(void *arg, PRBool reading) { - nsPKCS12Blob *cx = reinterpret_cast(arg); + auto cx = static_cast(arg); NS_ENSURE_TRUE(cx, SECFailure); - + if (reading) { NS_ENSURE_TRUE(cx->mDigest, SECFailure); @@ -557,17 +549,17 @@ nsPKCS12Blob::digest_open(void *arg, PRBool reading) SECStatus nsPKCS12Blob::digest_close(void *arg, PRBool remove_it) { - nsPKCS12Blob *cx = reinterpret_cast(arg); + auto cx = static_cast(arg); NS_ENSURE_TRUE(cx, SECFailure); delete cx->mDigestIterator; cx->mDigestIterator = nullptr; - if (remove_it) { + if (remove_it) { delete cx->mDigest; cx->mDigest = nullptr; } - + return SECSuccess; } @@ -576,7 +568,7 @@ nsPKCS12Blob::digest_close(void *arg, PRBool remove_it) int nsPKCS12Blob::digest_read(void *arg, unsigned char *buf, unsigned long len) { - nsPKCS12Blob *cx = reinterpret_cast(arg); + auto cx = static_cast(arg); NS_ENSURE_TRUE(cx, SECFailure); NS_ENSURE_TRUE(cx->mDigest, SECFailure); @@ -584,13 +576,13 @@ nsPKCS12Blob::digest_read(void *arg, unsigned char *buf, unsigned long len) NS_ENSURE_TRUE(cx->mDigestIterator, SECFailure); unsigned long available = cx->mDigestIterator->size_forward(); - + if (len > available) len = available; memcpy(buf, cx->mDigestIterator->get(), len); cx->mDigestIterator->advance(len); - + return len; } @@ -599,16 +591,16 @@ nsPKCS12Blob::digest_read(void *arg, unsigned char *buf, unsigned long len) int nsPKCS12Blob::digest_write(void *arg, unsigned char *buf, unsigned long len) { - nsPKCS12Blob *cx = reinterpret_cast(arg); + auto cx = static_cast(arg); NS_ENSURE_TRUE(cx, SECFailure); NS_ENSURE_TRUE(cx->mDigest, SECFailure); // make sure we are in write mode, read iterator has not yet been allocated NS_ENSURE_FALSE(cx->mDigestIterator, SECFailure); - - cx->mDigest->Append(reinterpret_cast(buf), - static_cast(len)); - + + cx->mDigest->Append(BitwiseCast(buf), + static_cast(len)); + return len; } @@ -654,10 +646,9 @@ nsPKCS12Blob::nickname_collision(SECItem *oldNick, PRBool *cancel, void *wincx) // without a corresponding cert. // XXX If a user imports *many* certs without the 'friendly name' // attribute, then this may take a long time. :( + nickname = nickFromPropC; if (count > 1) { - nickname.Adopt(PR_smprintf("%s #%d", nickFromPropC.get(), count)); - } else { - nickname = nickFromPropC; + nickname.AppendPrintf(" #%d", count); } CERTCertificate *cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), const_cast(nickname.get())); diff --git a/security/manager/ssl/nsPKCS12Blob.h b/security/manager/ssl/nsPKCS12Blob.h index 58eca08b20..d717a328a1 100644 --- a/security/manager/ssl/nsPKCS12Blob.h +++ b/security/manager/ssl/nsPKCS12Blob.h @@ -54,7 +54,7 @@ private: nsresult getPKCS12FilePassword(SECItem *); nsresult newPKCS12FilePassword(SECItem *); nsresult inputToDecoder(SEC_PKCS12DecoderContext *, nsIFile *); - void unicodeToItem(const char16_t *, SECItem *); + nsresult unicodeToItem(const char16_t *, SECItem *); void handleError(int myerr = 0); // RetryReason and ImportMode are used when importing a PKCS12 file. diff --git a/security/manager/ssl/nsRandomGenerator.cpp b/security/manager/ssl/nsRandomGenerator.cpp index 3672924515..ceda7fbd77 100644 --- a/security/manager/ssl/nsRandomGenerator.cpp +++ b/security/manager/ssl/nsRandomGenerator.cpp @@ -4,6 +4,7 @@ #include "nsRandomGenerator.h" +#include "ScopedNSSTypes.h" #include "nsNSSComponent.h" #include "pk11pub.h" #include "prerror.h" @@ -23,7 +24,7 @@ nsRandomGenerator::GenerateRandomBytes(uint32_t aLength, return NS_ERROR_NOT_AVAILABLE; } - mozilla::ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + mozilla::UniquePK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return NS_ERROR_FAILURE; } @@ -33,8 +34,7 @@ nsRandomGenerator::GenerateRandomBytes(uint32_t aLength, return NS_ERROR_OUT_OF_MEMORY; } - SECStatus srv = PK11_GenerateRandomOnSlot(slot, buf, aLength); - + SECStatus srv = PK11_GenerateRandomOnSlot(slot.get(), buf, aLength); if (srv != SECSuccess) { NS_Free(buf); return NS_ERROR_FAILURE; diff --git a/security/manager/ssl/nsSiteSecurityService.cpp b/security/manager/ssl/nsSiteSecurityService.cpp index f9282c6812..b1088ed06f 100644 --- a/security/manager/ssl/nsSiteSecurityService.cpp +++ b/security/manager/ssl/nsSiteSecurityService.cpp @@ -706,7 +706,7 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI, NS_ENSURE_TRUE(nssCert, NS_ERROR_FAILURE); mozilla::pkix::Time now(mozilla::pkix::Now()); - ScopedCERTCertList certList; + UniqueCERTCertList certList; RefPtr certVerifier(GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); if (certVerifier->VerifySSLServerCert(nssCert, nullptr, // stapled ocsp diff --git a/security/manager/ssl/nsSmartCardMonitor.cpp b/security/manager/ssl/nsSmartCardMonitor.cpp index da13f289b5..ace537dd05 100644 --- a/security/manager/ssl/nsSmartCardMonitor.cpp +++ b/security/manager/ssl/nsSmartCardMonitor.cpp @@ -1,14 +1,16 @@ /* 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 "nspr.h" +#include "nsSmartCardMonitor.h" + +#include "ScopedNSSTypes.h" #include "mozilla/Services.h" #include "mozilla/unused.h" #include "nsIObserverService.h" #include "nsServiceManagerUtils.h" -#include "nsSmartCardMonitor.h" #include "nsThreadUtils.h" +#include "nspr.h" #include "pk11func.h" using namespace mozilla; @@ -318,7 +320,6 @@ SmartCardMonitoringThread::SendEvent(const nsAString& eventType, // void SmartCardMonitoringThread::Execute() { - PK11SlotInfo* slot; const char* tokenName; // @@ -339,17 +340,18 @@ void SmartCardMonitoringThread::Execute() // loop starts.. do { - slot = SECMOD_WaitForAnyTokenEvent(mModule, 0, PR_SecondsToInterval(1)); + UniquePK11SlotInfo slot( + SECMOD_WaitForAnyTokenEvent(mModule, 0, PR_SecondsToInterval(1))); if (!slot) { break; } // now we have a potential insertion or removal event, see if the slot // is present to determine which it is... - if (PK11_IsPresent(slot)) { + if (PK11_IsPresent(slot.get())) { // insertion - CK_SLOT_ID slotID = PK11_GetSlotID(slot); - uint32_t series = PK11_GetSlotSeries(slot); + CK_SLOT_ID slotID = PK11_GetSlotID(slot.get()); + uint32_t series = PK11_GetSlotSeries(slot.get()); // skip spurious insertion events... if (series != GetTokenSeries(slotID)) { @@ -359,14 +361,14 @@ void SmartCardMonitoringThread::Execute() if (tokenName) { SendEvent(NS_LITERAL_STRING("smartcard-remove"), tokenName); } - tokenName = PK11_GetTokenName(slot); + tokenName = PK11_GetTokenName(slot.get()); // save the token name and series SetTokenName(slotID, tokenName, series); SendEvent(NS_LITERAL_STRING("smartcard-insert"), tokenName); } } else { // retrieve token name - CK_SLOT_ID slotID = PK11_GetSlotID(slot); + CK_SLOT_ID slotID = PK11_GetSlotID(slot.get()); tokenName = GetTokenName(slotID); // if there's not a token name, then the software isn't expecting // a (or another) remove event. @@ -376,8 +378,6 @@ void SmartCardMonitoringThread::Execute() SetTokenName(slotID, nullptr, 0); } } - PK11_FreeSlot(slot); - } while (1); } diff --git a/security/manager/ssl/nsUsageArrayHelper.cpp b/security/manager/ssl/nsUsageArrayHelper.cpp index 5576065021..124303ceaf 100644 --- a/security/manager/ssl/nsUsageArrayHelper.cpp +++ b/security/manager/ssl/nsUsageArrayHelper.cpp @@ -4,6 +4,7 @@ #include "nsUsageArrayHelper.h" +#include "ScopedNSSTypes.h" #include "mozilla/Assertions.h" #include "nsCOMPtr.h" #include "nsComponentManagerUtils.h" @@ -103,7 +104,7 @@ nsUsageArrayHelper::check(uint32_t previousCheckResult, MOZ_CRASH("unknown cert usage passed to check()"); } - ScopedCERTCertList unusedBuiltChain; + UniqueCERTCertList unusedBuiltChain; SECStatus rv = certVerifier->VerifyCert(mCert, aCertUsage, time, nullptr /*XXX:wincx*/, nullptr /*hostname*/, diff --git a/security/manager/ssl/tests/compiled/TestMD4.cpp b/security/manager/ssl/tests/compiled/TestMD4.cpp index 8c82cc8adb..69d4bd83aa 100644 --- a/security/manager/ssl/tests/compiled/TestMD4.cpp +++ b/security/manager/ssl/tests/compiled/TestMD4.cpp @@ -6,6 +6,7 @@ #include "TestHarness.h" #include "md4.h" +#include "mozilla/Casting.h" // The md4 implementation isn't built as a separate library. This is the easiest // way to expose the symbols necessary to test the implementation. @@ -52,7 +53,8 @@ TestMD4() for (size_t i = 0; i < MOZ_ARRAY_LENGTH(rfc1320_test_values); i++) { uint8_t md4_result[16]; - md4sum(reinterpret_cast(rfc1320_test_values[i].data), + md4sum(mozilla::BitwiseCast( + rfc1320_test_values[i].data), strlen(rfc1320_test_values[i].data), md4_result); if (memcmp(md4_result, rfc1320_test_values[i].md4, 16) != 0) { fail("MD4 comparison test value #%d from RFC1320 failed", i + 1); diff --git a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp index c524b185b7..5adad52b6c 100644 --- a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp +++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp @@ -7,6 +7,7 @@ #include "CertVerifier.h" #include "OCSPCache.h" #include "gtest/gtest.h" +#include "mozilla/Casting.h" #include "mozilla/Snprintf.h" #include "nss.h" #include "pkix/pkixtypes.h" @@ -22,6 +23,9 @@ template inline Input LiteralInput(const char(&valueString)[N]) { + // Ideally we would use mozilla::BitwiseCast() here rather than + // reinterpret_cast for better type checking, but the |N - 1| part trips + // static asserts. return Input(reinterpret_cast(valueString)); } @@ -85,7 +89,8 @@ TEST_F(OCSPCacheTest, TestVariousGets) SCOPED_TRACE(""); for (int i = 0; i < MaxCacheEntries; i++) { uint8_t serialBuf[8]; - snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); + snprintf(mozilla::BitwiseCast(serialBuf), sizeof(serialBuf), + "%04d", i); Input fakeSerial; ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4)); Time timeIn(now); @@ -134,7 +139,8 @@ TEST_F(OCSPCacheTest, TestEviction) // we cause the least recently used entry to be evicted. for (int i = 0; i < MaxCacheEntries + 1; i++) { uint8_t serialBuf[8]; - snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); + snprintf(mozilla::BitwiseCast(serialBuf), sizeof(serialBuf), + "%04d", i); Input fakeSerial; ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4)); Time timeIn(now); @@ -159,7 +165,8 @@ TEST_F(OCSPCacheTest, TestNoEvictionForRevokedResponses) // we cause the least recently used entry that isn't revoked to be evicted. for (int i = 1; i < MaxCacheEntries + 1; i++) { uint8_t serialBuf[8]; - snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); + snprintf(mozilla::BitwiseCast(serialBuf), sizeof(serialBuf), + "%04d", i); Input fakeSerial; ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4)); Time timeIn(now); @@ -185,7 +192,8 @@ TEST_F(OCSPCacheTest, TestEverythingIsRevoked) // Fill up the cache with revoked responses. for (int i = 0; i < MaxCacheEntries; i++) { uint8_t serialBuf[8]; - snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); + snprintf(mozilla::BitwiseCast(serialBuf), sizeof(serialBuf), + "%04d", i); Input fakeSerial; ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4)); Time timeIn(now); diff --git a/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows.pfx b/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows.pfx new file mode 100644 index 0000000000000000000000000000000000000000..e969d672d7357f1131c5e0f486d263aa07066825 GIT binary patch literal 2041 zcmY*Zc{tQ-8-B-ZGPazNrNu~RGPZtWX=HCmWS1yW*BH#uW*Z$dM`i5UiI`+bq!48( zMNFw==};#LWtn3bkxptXrM_QX-#K09kLP{w=YH?ITzTLjZQh>NoRPYGp+;-Vm!)`3G4gZqD%v=l<3go`9XT;yhG14il} z8Xhi-l1vG`E2N7;KS@Yn`%qjYNVuoy(e|yXtEgT)pAd;_p} zMbNGYS1Dhl;qZO#@eGSX(>LECaqO0aqWd{ zpK`uR)ElD~p^y7vsMko=Y6x>H!0K|Ixys{fUmW>n$GpX2>1XmH-cU(!`FYK|yLGSN z+D-H|F=iBZYy-Q%MuYjj%~PN z20NLnp(VCB%#PH-1dTtws$8);+~R5UHoQr#tm0l>)j*r%S;A2<-WuoEG0eB32(Kb1 z8Z*2ZUiP9U|5%>Z>t2>)kiz*Lo5pivcO|x#{*7Uo4>62a2x^x~GUIPAugg#4TFMCg z``@#M+WdAO>&iZ<8Hu!*wnE<(GtxA>iHKw7(cLK1UP5bK{WX=kV&a9T*9Eneh^K{* zN=c4;M1=R|#Lj!6u6pT%9-NiidVXvY)|HW@ak6PSjuQR7aOGL#u{O63PrQu7GlV|!S0u#RVD>uZ6N9>m}cPyS= zpEE5)0y$N3%sWU1vql?(Kq0u4wGw0i{qpaYtx+UnP$&cd06O@S#FL#jIRXyY2@HWP zpaOkh0|y1@f*o*X4YtF`5YBf1V;FsBY{9M{hzxVK0D{$2xNZz18(0|)9r^hH1o_Y= zAK(On03C#Z6CfOpKo9{4@QCAp325+12LZqzW~^aF6bONNIn zN(z@=5!dnhzbzfmmI;+g*ytLV=)HWWK+PmD3|$^H6wis{jgK;V?@VSBfg-qX#Dy-ao$E zv@^*%q@lMNuv@Pa9lIEb+V?5>`C+v$r`Ad0CAc&kChGLUQoH#^-ZvMU4(W~qc43P} z$&GCa&}vMG>Y=`<{nnEo#+PTFROC0Avi~^2Rr;+_Uk&l0pf`5&$40`r73C$8$8LX? zPQ{zC#&b@N7|mtY+Jb;QHFk~ojqO9RL3ONCL;Zt9eG;9R_HL^({aJ5#&Wf$`2UdjV zI7#w?3bZlh8aquxSf_tl=no=UMv&fbCShd5>N#6%X58Qwdaie}oSc-poMs*9YjQ%?JiWvwl-;YhQnE zb30Q`RiCR%#hKop1Y$CGcp#+ynd|)i*_OAhGw63BUvk5Xz|M*jC9dvzg{-NQ%ygN( zSEL@;XLoFr<)f(}kf*ymHT>-jEr9E7Hln8Hp=*2Dbm974J0^BWvs%dRD zs_nOySF(&*%u7Ur1GUnf(CF;-@+a;}J5$l}99|@|%Jb&K6hq4p$BR+*K`)AO*rxpx zFHAmF4mrP3MW57OK%npFYXWRGrB$PxK|<8gV#k<7m?=95%`F>h&^z-R{`7^NhQmK { + ok(!gGetPKCS12Password, + "getPKCS12FilePassword should be called only once."); + + password.value = TEST_CERT_PASSWORD; + do_print("getPKCS12FilePassword() is called"); + gGetPKCS12Password = true; + return true; + }, +}; + +const gPrompt = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]), + alert: (title, text) => { + do_print("alert('" + text + "')"); + }, +}; + +const gPromptFactory = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory]), + getPrompt: (aWindow, aIID) => gPrompt, +}; + +function doesCertExist(commonName) { + let allCerts = gCertDB.getCerts(); + let enumerator = allCerts.getEnumerator(); + while (enumerator.hasMoreElements()) { + let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert); + if (cert.isBuiltInRoot) { + continue; + } + if (cert.commonName == commonName) { + return true; + } + } + + return false; +} + +function testImportPKCS12Cert() { + ok(!doesCertExist(CERT_COMMON_NAME), + "Cert should not be in the database before import"); + + // Import and check for success. + let certFile = do_get_file("test_certDB_import/cert_from_windows.pfx"); + gCertDB.importPKCS12File(null, certFile); + + ok(gGetPKCS12Password, "PKCS12 password should be asked"); + + ok(doesCertExist(CERT_COMMON_NAME), + "Cert should now be found in the database"); +} + +function run_test() { + // We have to set a password and login before we attempt to import anything. + loginToDBWithDefaultPassword(); + + let certificateDialogsCID = + MockRegistrar.register("@mozilla.org/nsCertificateDialogs;1", + gCertificateDialogs); + let promptFactoryCID = + MockRegistrar.register("@mozilla.org/prompter;1", gPromptFactory); + + do_register_cleanup(() => { + MockRegistrar.unregister(certificateDialogsCID); + MockRegistrar.unregister(promptFactoryCID); + }); + + // Import PKCS12 file with utf-8 password + testImportPKCS12Cert(); +} diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp index 5816eed38e..1701e54ddb 100644 --- a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp @@ -8,7 +8,6 @@ #include #include -#include "ScopedNSSTypes.h" #include "base64.h" #include "mozilla/Move.h" #include "mozilla/Snprintf.h" @@ -130,7 +129,7 @@ AddKeyFromFile(const char* basePath, const char* filename) unsigned int binLength; UniquePORTString bin( - reinterpret_cast(ATOB_AsciiToData(base64, &binLength))); + BitwiseCast(ATOB_AsciiToData(base64, &binLength))); if (!bin || binLength == 0) { PrintPRError("ATOB_AsciiToData failed"); return SECFailure; @@ -141,21 +140,21 @@ AddKeyFromFile(const char* basePath, const char* filename) return SECFailure; } PORT_Memcpy(secitem->data, bin.get(), binLength); - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { PrintPRError("PK11_GetInternalKeySlot failed"); return SECFailure; } - if (PK11_NeedUserInit(slot)) { - if (PK11_InitPin(slot, nullptr, nullptr) != SECSuccess) { + if (PK11_NeedUserInit(slot.get())) { + if (PK11_InitPin(slot.get(), nullptr, nullptr) != SECSuccess) { PrintPRError("PK11_InitPin failed"); return SECFailure; } } SECKEYPrivateKey* privateKey; - if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, secitem, nullptr, nullptr, - true, false, KU_ALL, - &privateKey, nullptr) + if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot.get(), secitem.get(), + nullptr, nullptr, true, false, + KU_ALL, &privateKey, nullptr) != SECSuccess) { PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed"); return SECFailure; @@ -198,7 +197,7 @@ AddCertificateFromFile(const char* basePath, const char* filename) PrintPRError("CERT_NewTempCertificate failed"); return SECFailure; } - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { PrintPRError("PK11_GetInternalKeySlot failed"); return SECFailure; @@ -448,7 +447,7 @@ ConfigSecureServerWithNamedCert(PRFileDesc* fd, const char* certName, certList->arena = arena.release(); // We also have to manually copy the certificates we care about to the // list, because there aren't any utility functions for that either. - certList->certs = reinterpret_cast( + certList->certs = static_cast( PORT_ArenaAlloc(certList->arena, 2 * sizeof(SECItem))); if (SECITEM_CopyItem(certList->arena, certList->certs, &cert->derCert) != SECSuccess) { @@ -463,7 +462,11 @@ ConfigSecureServerWithNamedCert(PRFileDesc* fd, const char* certName, certList->len = 2; } - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); + if (!slot) { + PrintPRError("PK11_GetInternalKeySlot failed"); + return SECFailure; + } UniqueSECKEYPrivateKey key( PK11_FindKeyByDERCert(slot.get(), cert.get(), nullptr)); if (!key) { diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h index eff86ca38b..f1dc1569a2 100644 --- a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h +++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h @@ -14,8 +14,10 @@ // it will connect to a specified port and issue a simple HTTP request. #include -#include "prio.h" + #include "ScopedNSSTypes.h" +#include "mozilla/Casting.h" +#include "prio.h" #include "secerr.h" #include "ssl.h" @@ -63,7 +65,7 @@ GetHostForSNI(const SECItem *aSrvNameArr, uint32_t aSrvNameArrSize, for (uint32_t i = 0; i < aSrvNameArrSize; i++) { for (const Host *host = hosts; host->mHostName; ++host) { SECItem hostName; - hostName.data = reinterpret_cast(const_cast(host->mHostName)); + hostName.data = BitwiseCast(host->mHostName); hostName.len = strlen(host->mHostName); if (SECITEM_ItemsAreEqual(&hostName, &aSrvNameArr[i])) { if (gDebugLevel >= DEBUG_VERBOSE) { diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini index 7e697d3931..76856729e9 100644 --- a/security/manager/ssl/tests/unit/xpcshell.ini +++ b/security/manager/ssl/tests/unit/xpcshell.ini @@ -72,6 +72,7 @@ run-sequentially = hardcoded ports [test_cert_trust.js] [test_cert_version.js] [test_certDB_import.js] +[test_certDB_import_pkcs12.js] [test_certviewer_invalid_oids.js] skip-if = toolkit == 'android' || buildapp == 'b2g' [test_constructX509FromBase64.js] diff --git a/storage/mozStorageConnection.cpp b/storage/mozStorageConnection.cpp index 64825f11ff..95ff936436 100644 --- a/storage/mozStorageConnection.cpp +++ b/storage/mozStorageConnection.cpp @@ -367,7 +367,7 @@ public: MOZ_ASSERT(onAsyncThread); #endif // DEBUG - nsCOMPtr event = NS_NewRunnableMethodWithArg> + nsCOMPtr event = NewRunnableMethod> (mConnection, &Connection::shutdownAsyncThread, mAsyncExecutionThread); (void)NS_DispatchToMainThread(event); diff --git a/storage/mozStorageService.cpp b/storage/mozStorageService.cpp index ee38a096d6..7ba2e4ad10 100644 --- a/storage/mozStorageService.cpp +++ b/storage/mozStorageService.cpp @@ -383,7 +383,7 @@ Service::minimizeMemory() // We are on the wrong thread, the query should be executed on the // opener thread, so we must dispatch to it. nsCOMPtr event = - NS_NewRunnableMethodWithArg( + NewRunnableMethod( conn, &Connection::ExecuteSimpleSQL, shrinkPragma); conn->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL); } @@ -702,7 +702,7 @@ public: : mConnection->initialize(); if (NS_FAILED(rv)) { nsCOMPtr closeRunnable = - NS_NewRunnableMethodWithArg( + NewRunnableMethod( mConnection.get(), &Connection::AsyncClose, nullptr); diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index d07985c9ae..dbbcc5185a 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -23595,6 +23595,12 @@ "url": "/XMLHttpRequest/open-url-multi-window-6.htm" } ], + "html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html": [ + { + "path": "html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html", + "url": "/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html" + } + ], "html/semantics/document-metadata/the-base-element/base_about_blank.html": [ { "path": "html/semantics/document-metadata/the-base-element/base_about_blank.html", diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html new file mode 100644 index 0000000000..f72ed1eaf2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html @@ -0,0 +1,197 @@ + + + + + + Location objects' custom [[GetPrototypeOf]] trap permit [[Prototype]] chain cycles to be created through them + + + + + + + + + +

+ +
+ + + + + + + diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/cross_origin_joined_frame.sub.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/cross_origin_joined_frame.sub.html new file mode 100644 index 0000000000..a3ffdd005a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/cross_origin_joined_frame.sub.html @@ -0,0 +1,15 @@ + + + + + Cross-origin subframe for Location cyclic [[Prototype]] test + + + + + +

Cross-origin iframe with joined document.domain

+ + diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/same_origin_frame.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/same_origin_frame.html new file mode 100644 index 0000000000..953e696b2a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/same_origin_frame.html @@ -0,0 +1,12 @@ + + + + + Same-origin subframe for Location cyclic [[Prototype]] test + + + + +

Same-origin iframe

+ + diff --git a/toolkit/components/filewatcher/NativeFileWatcherWin.cpp b/toolkit/components/filewatcher/NativeFileWatcherWin.cpp index ac9a7654d0..21385b03bf 100644 --- a/toolkit/components/filewatcher/NativeFileWatcherWin.cpp +++ b/toolkit/components/filewatcher/NativeFileWatcherWin.cpp @@ -1311,7 +1311,7 @@ NativeFileWatcherService::AddPath(const nsAString& aPathToWatch, nsMainThreadPtrHandle successCallbackHandle( new nsMainThreadPtrHolder(aOnSuccess)); - // Wrap the path and the callbacks in order to pass them using NS_NewRunnableMethodWithArg. + // Wrap the path and the callbacks in order to pass them using NewRunnableMethod. UniquePtr wrappedCallbacks( new PathRunnablesParametersWrapper( aPathToWatch, @@ -1322,7 +1322,7 @@ NativeFileWatcherService::AddPath(const nsAString& aPathToWatch, // Since this function does a bit of I/O stuff , run it in the IO thread. nsresult rv = mIOThread->Dispatch( - NS_NewRunnableMethodWithArg( + NewRunnableMethod( static_cast(mWorkerIORunnable.get()), &NativeFileWatcherIOTask::AddPathRunnableMethod, wrappedCallbacks.get()), @@ -1381,7 +1381,7 @@ NativeFileWatcherService::RemovePath(const nsAString& aPathToRemove, nsMainThreadPtrHandle successCallbackHandle( new nsMainThreadPtrHolder(aOnSuccess)); - // Wrap the path and the callbacks in order to pass them using NS_NewRunnableMethodWithArg. + // Wrap the path and the callbacks in order to pass them using NewRunnableMethod. UniquePtr wrappedCallbacks( new PathRunnablesParametersWrapper( aPathToRemove, @@ -1392,7 +1392,7 @@ NativeFileWatcherService::RemovePath(const nsAString& aPathToRemove, // Since this function does a bit of I/O stuff, run it in the IO thread. nsresult rv = mIOThread->Dispatch( - NS_NewRunnableMethodWithArg( + NewRunnableMethod( static_cast(mWorkerIORunnable.get()), &NativeFileWatcherIOTask::RemovePathRunnableMethod, wrappedCallbacks.get()), @@ -1441,7 +1441,7 @@ NativeFileWatcherService::Uninit() // in the IO thread. nsresult rv = ioThread->Dispatch( - NS_NewRunnableMethod( + NewRunnableMethod( static_cast(mWorkerIORunnable.get()), &NativeFileWatcherIOTask::DeactivateRunnableMethod), nsIEventTarget::DISPATCH_NORMAL); diff --git a/toolkit/components/places/AsyncFaviconHelpers.cpp b/toolkit/components/places/AsyncFaviconHelpers.cpp index 63da443f00..200a51838e 100644 --- a/toolkit/components/places/AsyncFaviconHelpers.cpp +++ b/toolkit/components/places/AsyncFaviconHelpers.cpp @@ -365,14 +365,24 @@ AsyncFaviconHelperBase::~AsyncFaviconHelperBase() //////////////////////////////////////////////////////////////////////////////// //// AsyncFetchAndSetIconForPage +NS_IMPL_ISUPPORTS_INHERITED( + AsyncFetchAndSetIconForPage +, Runnable +, nsIStreamListener +, nsIInterfaceRequestor +, nsIChannelEventSink +, mozIPlacesPendingOperation +) + // static nsresult AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI, nsIURI* aPageURI, enum AsyncFaviconFetchMode aFetchMode, - uint32_t aFaviconLoadType, + bool aFaviconLoadPrivate, nsIFaviconDataCallback* aCallback, - nsIPrincipal* aLoadingPrincipal) + nsIPrincipal* aLoadingPrincipal, + mozIPlacesPendingOperation** _canceler) { NS_ENSURE_ARG(aLoadingPrincipal); NS_PRECONDITION(NS_IsMainThread(), @@ -388,7 +398,7 @@ AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI, NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY); rv = navHistory->CanAddURI(aPageURI, &canAddToHistory); NS_ENSURE_SUCCESS(rv, rv); - page.canAddToHistory = !!canAddToHistory && aFaviconLoadType != nsIFaviconService::FAVICON_LOAD_PRIVATE; + page.canAddToHistory = !!canAddToHistory && !aFaviconLoadPrivate; IconData icon; @@ -419,7 +429,7 @@ AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI, // The event will swap owning pointers, thus we need a new pointer. nsCOMPtr callback(aCallback); RefPtr event = - new AsyncFetchAndSetIconForPage(icon, page, aFaviconLoadType, + new AsyncFetchAndSetIconForPage(icon, page, aFaviconLoadPrivate, callback, aLoadingPrincipal); // Get the target thread and start the work. @@ -427,24 +437,24 @@ AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI, NS_ENSURE_STATE(DB); DB->DispatchToAsyncThread(event); + // Return this event to the caller to allow aborting an eventual fetch. + event.forget(_canceler); + return NS_OK; } AsyncFetchAndSetIconForPage::AsyncFetchAndSetIconForPage( IconData& aIcon , PageData& aPage -, uint32_t aFaviconLoadType +, bool aFaviconLoadPrivate , nsCOMPtr& aCallback , nsIPrincipal* aLoadingPrincipal ) : AsyncFaviconHelperBase(aCallback) , mIcon(aIcon) , mPage(aPage) - , mFaviconLoadPrivate(aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE) + , mFaviconLoadPrivate(aFaviconLoadPrivate) , mLoadingPrincipal(new nsMainThreadPtrHolder(aLoadingPrincipal)) -{ -} - -AsyncFetchAndSetIconForPage::~AsyncFetchAndSetIconForPage() + , mCanceled(false) { } @@ -474,57 +484,22 @@ AsyncFetchAndSetIconForPage::Run() return NS_OK; } - else { - // Fetch the icon from network. When done this will associate the - // icon to the page and notify. - RefPtr event = - new AsyncFetchAndSetIconFromNetwork(mIcon, mPage, mFaviconLoadPrivate, - mCallback, mLoadingPrincipal); - // Start the work on the main thread. - rv = NS_DispatchToMainThread(event); - NS_ENSURE_SUCCESS(rv, rv); + // Fetch the icon from the network, the request starts from the main-thread. + // When done this will associate the icon to the page and notify. + nsCOMPtr event = + NewRunnableMethod(this, &AsyncFetchAndSetIconForPage::FetchFromNetwork); + return NS_DispatchToMainThread(event); +} + +nsresult +AsyncFetchAndSetIconForPage::FetchFromNetwork() { + MOZ_ASSERT(NS_IsMainThread()); + + if (mCanceled) { + return NS_OK; } - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// -//// AsyncFetchAndSetIconFromNetwork - -NS_IMPL_ISUPPORTS_INHERITED( - AsyncFetchAndSetIconFromNetwork -, Runnable -, nsIStreamListener -, nsIInterfaceRequestor -, nsIChannelEventSink -) - -AsyncFetchAndSetIconFromNetwork::AsyncFetchAndSetIconFromNetwork( - IconData& aIcon -, PageData& aPage -, bool aFaviconLoadPrivate -, nsCOMPtr& aCallback -, const nsMainThreadPtrHandle& aLoadingPrincipal -) -: AsyncFaviconHelperBase(aCallback) -, mIcon(aIcon) -, mPage(aPage) -, mFaviconLoadPrivate(aFaviconLoadPrivate) -, mLoadingPrincipal(aLoadingPrincipal) -{ -} - -AsyncFetchAndSetIconFromNetwork::~AsyncFetchAndSetIconFromNetwork() -{ -} - -NS_IMETHODIMP -AsyncFetchAndSetIconFromNetwork::Run() -{ - NS_PRECONDITION(NS_IsMainThread(), - "This should be called on the main thread"); - // Ensure data is cleared, since it's going to be overwritten. if (mIcon.data.Length() > 0) { mIcon.data.Truncate(0); @@ -562,18 +537,36 @@ AsyncFetchAndSetIconFromNetwork::Run() } NS_IMETHODIMP -AsyncFetchAndSetIconFromNetwork::OnStartRequest(nsIRequest* aRequest, - nsISupports* aContext) +AsyncFetchAndSetIconForPage::Cancel() { + MOZ_ASSERT(NS_IsMainThread()); + if (mCanceled) { + return NS_ERROR_UNEXPECTED; + } + mCanceled = true; + if (mRequest) { + mRequest->Cancel(NS_BINDING_ABORTED); + } return NS_OK; } NS_IMETHODIMP -AsyncFetchAndSetIconFromNetwork::OnDataAvailable(nsIRequest* aRequest, - nsISupports* aContext, - nsIInputStream* aInputStream, - uint64_t aOffset, - uint32_t aCount) +AsyncFetchAndSetIconForPage::OnStartRequest(nsIRequest* aRequest, + nsISupports* aContext) +{ + mRequest = aRequest; + if (mCanceled) { + mRequest->Cancel(NS_BINDING_ABORTED); + } + return NS_OK; +} + +NS_IMETHODIMP +AsyncFetchAndSetIconForPage::OnDataAvailable(nsIRequest* aRequest, + nsISupports* aContext, + nsIInputStream* aInputStream, + uint64_t aOffset, + uint32_t aCount) { const size_t kMaxFaviconDownloadSize = 1 * 1024 * 1024; if (mIcon.data.Length() + aCount > kMaxFaviconDownloadSize) { @@ -597,7 +590,7 @@ AsyncFetchAndSetIconFromNetwork::OnDataAvailable(nsIRequest* aRequest, NS_IMETHODIMP -AsyncFetchAndSetIconFromNetwork::GetInterface(const nsIID& uuid, +AsyncFetchAndSetIconForPage::GetInterface(const nsIID& uuid, void** aResult) { return QueryInterface(uuid, aResult); @@ -605,7 +598,7 @@ AsyncFetchAndSetIconFromNetwork::GetInterface(const nsIID& uuid, NS_IMETHODIMP -AsyncFetchAndSetIconFromNetwork::AsyncOnChannelRedirect( +AsyncFetchAndSetIconForPage::AsyncOnChannelRedirect( nsIChannel* oldChannel , nsIChannel* newChannel , uint32_t flags @@ -617,12 +610,18 @@ AsyncFetchAndSetIconFromNetwork::AsyncOnChannelRedirect( } NS_IMETHODIMP -AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest, - nsISupports* aContext, - nsresult aStatusCode) +AsyncFetchAndSetIconForPage::OnStopRequest(nsIRequest* aRequest, + nsISupports* aContext, + nsresult aStatusCode) { MOZ_ASSERT(NS_IsMainThread()); + // Don't need to track this anymore. + mRequest = nullptr; + if (mCanceled) { + return NS_OK; + } + nsFaviconService* favicons = nsFaviconService::GetFaviconService(); NS_ENSURE_STATE(favicons); @@ -640,7 +639,6 @@ AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest, nsCOMPtr channel = do_QueryInterface(aRequest); // aRequest should always QI to nsIChannel. - // See AsyncFetchAndSetIconFromNetwork::Run() MOZ_ASSERT(channel); nsAutoCString contentType; diff --git a/toolkit/components/places/AsyncFaviconHelpers.h b/toolkit/components/places/AsyncFaviconHelpers.h index 943bafaee0..54bb1c55d4 100644 --- a/toolkit/components/places/AsyncFaviconHelpers.h +++ b/toolkit/components/places/AsyncFaviconHelpers.h @@ -11,6 +11,7 @@ #include "nsIChannelEventSink.h" #include "nsIInterfaceRequestor.h" #include "nsIStreamListener.h" +#include "mozIPlacesPendingOperation.h" #include "nsThreadUtils.h" #include "nsProxyRelease.h" @@ -113,10 +114,20 @@ protected: * Async fetches icon from database or network, associates it with the required * page and finally notifies the change. */ -class AsyncFetchAndSetIconForPage : public AsyncFaviconHelperBase -{ -public: +class AsyncFetchAndSetIconForPage final : public AsyncFaviconHelperBase + , public nsIStreamListener + , public nsIInterfaceRequestor + , public nsIChannelEventSink + , public mozIPlacesPendingOperation + { + public: + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSICHANNELEVENTSINK + NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSIRUNNABLE + NS_DECL_MOZIPLACESPENDINGOPERATION + NS_DECL_ISUPPORTS_INHERITED /** * Creates the event and dispatches it to the async thread. @@ -128,6 +139,8 @@ public: * @param aFetchMode * Specifies whether a icon should be fetched from network if not found * in the database. + * @param aFaviconLoadPrivate + * Whether this favicon load is in private browsing. * @param aCallback * Function to be called when the fetch-and-associate process finishes. * @param aLoadingPrincipal @@ -136,9 +149,10 @@ public: static nsresult start(nsIURI* aFaviconURI, nsIURI* aPageURI, enum AsyncFaviconFetchMode aFetchMode, - uint32_t aFaviconLoadType, + bool aFaviconLoadPrivate, nsIFaviconDataCallback* aCallback, - nsIPrincipal* aLoadingPrincipal); + nsIPrincipal* aLoadingPrincipal, + mozIPlacesPendingOperation ** _canceler); /** * Constructor. @@ -147,6 +161,8 @@ public: * Icon to be fetched and associated. * @param aPage * Page to which associate the icon. + * @param aFaviconLoadPrivate + * Whether this favicon load is in private browsing. * @param aCallback * Function to be called when the fetch-and-associate process finishes. * @param aLoadingPrincipal @@ -154,62 +170,20 @@ public: */ AsyncFetchAndSetIconForPage(IconData& aIcon, PageData& aPage, - uint32_t aFaviconLoadType, + bool aFaviconLoadPrivate, nsCOMPtr& aCallback, nsIPrincipal* aLoadingPrincipal); - virtual ~AsyncFetchAndSetIconForPage(); - -protected: - IconData mIcon; - PageData mPage; - const bool mFaviconLoadPrivate; - nsMainThreadPtrHandle mLoadingPrincipal; -}; - -/** - * If needed will asynchronously fetch the icon from the network. It will - * finally dispatch an event to the async thread to associate the icon with - * the required page. - */ -class AsyncFetchAndSetIconFromNetwork : public AsyncFaviconHelperBase - , public nsIStreamListener - , public nsIInterfaceRequestor - , public nsIChannelEventSink -{ -public: - NS_DECL_NSISTREAMLISTENER - NS_DECL_NSIINTERFACEREQUESTOR - NS_DECL_NSICHANNELEVENTSINK - NS_DECL_NSIREQUESTOBSERVER - NS_DECL_NSIRUNNABLE - NS_DECL_ISUPPORTS_INHERITED - - /** - * Constructor. - * - * @param aIcon - * Icon to be fetched and associated. - * @param aPage - * Page to which associate the icon. - * @param aCallback - * Function to be called when the fetch-and-associate process finishes. - * @param aLoadingPrincipal - * LoadingPrincipal of the icon to be fetched. - */ - AsyncFetchAndSetIconFromNetwork(IconData& aIcon, - PageData& aPage, - bool aFaviconLoadPrivate, - nsCOMPtr& aCallback, - const nsMainThreadPtrHandle& aLoadingPrincipal); - -protected: - virtual ~AsyncFetchAndSetIconFromNetwork(); +private: + nsresult FetchFromNetwork(); + virtual ~AsyncFetchAndSetIconForPage() {} IconData mIcon; PageData mPage; const bool mFaviconLoadPrivate; nsMainThreadPtrHandle mLoadingPrincipal; + bool mCanceled; + nsCOMPtr mRequest; }; /** diff --git a/toolkit/components/places/History.cpp b/toolkit/components/places/History.cpp index a3c2f77acf..73c3309721 100644 --- a/toolkit/components/places/History.cpp +++ b/toolkit/components/places/History.cpp @@ -504,9 +504,7 @@ public: RefPtr cb = new VisitedQuery(aURI, callback, true); NS_ENSURE_TRUE(cb, NS_ERROR_OUT_OF_MEMORY); // As per IHistory contract, we must notify asynchronously. - nsCOMPtr event = - NS_NewRunnableMethod(cb, &VisitedQuery::NotifyVisitedStatus); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(cb, &VisitedQuery::NotifyVisitedStatus)); return NS_OK; } diff --git a/toolkit/components/places/moz.build b/toolkit/components/places/moz.build index f4281b20aa..0cf65c85fd 100644 --- a/toolkit/components/places/moz.build +++ b/toolkit/components/places/moz.build @@ -20,6 +20,7 @@ if CONFIG['MOZ_PLACES']: 'mozIAsyncLivemarks.idl', 'mozIColorAnalyzer.idl', 'mozIPlacesAutoComplete.idl', + 'mozIPlacesPendingOperation.idl', 'nsIAnnotationService.idl', 'nsIBrowserHistory.idl', 'nsIFaviconService.idl', diff --git a/toolkit/components/places/mozIAsyncFavicons.idl b/toolkit/components/places/mozIAsyncFavicons.idl index 86d0aa505c..a4e9ebee1e 100644 --- a/toolkit/components/places/mozIAsyncFavicons.idl +++ b/toolkit/components/places/mozIAsyncFavicons.idl @@ -8,6 +8,7 @@ interface nsIURI; interface nsIFaviconDataCallback; interface nsIPrincipal; +interface mozIPlacesPendingOperation; [scriptable, uuid(a9c81797-9133-4823-b55f-3646e67cfd41)] interface mozIAsyncFavicons : nsISupports @@ -56,13 +57,13 @@ interface mozIAsyncFavicons : nsISupports * * @see nsIFaviconDataCallback in nsIFaviconService.idl. */ - void setAndFetchFaviconForPage(in nsIURI aPageURI, - in nsIURI aFaviconURI, - in boolean aForceReload, - in unsigned long aFaviconLoadType, - [optional] in nsIFaviconDataCallback aCallback, - [optional] in nsIPrincipal aLoadingPrincipal); - + mozIPlacesPendingOperation setAndFetchFaviconForPage( + in nsIURI aPageURI, + in nsIURI aFaviconURI, + in boolean aForceReload, + in unsigned long aFaviconLoadType, + [optional] in nsIFaviconDataCallback aCallback, + [optional] in nsIPrincipal aLoadingPrincipal); /** * Sets the data for a given favicon URI either by replacing existing data in * the database or taking the place of otherwise fetched icon data when diff --git a/toolkit/components/places/mozIPlacesPendingOperation.idl b/toolkit/components/places/mozIPlacesPendingOperation.idl new file mode 100644 index 0000000000..678a908708 --- /dev/null +++ b/toolkit/components/places/mozIPlacesPendingOperation.idl @@ -0,0 +1,14 @@ +/* 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 "nsISupports.idl" + +[scriptable, uuid(ebd31374-3808-40e4-9e73-303bf70467c3)] +interface mozIPlacesPendingOperation : nsISupports { + /** + * Cancels a pending operation, if possible. This will only fail if you try + * to cancel more than once. + */ + void cancel(); +}; diff --git a/toolkit/components/places/nsFaviconService.cpp b/toolkit/components/places/nsFaviconService.cpp index c918799ab0..118decdf83 100644 --- a/toolkit/components/places/nsFaviconService.cpp +++ b/toolkit/components/places/nsFaviconService.cpp @@ -212,10 +212,12 @@ nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI, bool aForceReload, uint32_t aFaviconLoadType, nsIFaviconDataCallback* aCallback, - nsIPrincipal* aLoadingPrincipal) + nsIPrincipal* aLoadingPrincipal, + mozIPlacesPendingOperation **_canceler) { NS_ENSURE_ARG(aPageURI); NS_ENSURE_ARG(aFaviconURI); + NS_ENSURE_ARG_POINTER(_canceler); // If a favicon is in the failed cache, only load it during a forced reload. bool previouslyFailed; @@ -239,9 +241,10 @@ nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI, // Check if the icon already exists and fetch it from the network, if needed. // Finally associate the icon to the requested page if not yet associated. + bool loadPrivate = aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE; rv = AsyncFetchAndSetIconForPage::start( aFaviconURI, aPageURI, aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING, - aFaviconLoadType, aCallback, loadingPrincipal + loadPrivate, aCallback, loadingPrincipal, _canceler ); NS_ENSURE_SUCCESS(rv, rv); diff --git a/toolkit/components/satchel/nsFormFillController.cpp b/toolkit/components/satchel/nsFormFillController.cpp index ddee81cd9d..86f9ac46af 100644 --- a/toolkit/components/satchel/nsFormFillController.cpp +++ b/toolkit/components/satchel/nsFormFillController.cpp @@ -119,7 +119,7 @@ nsFormFillController::AttributeChanged(nsIDocument* aDocument, // to avoid ending up in an endless loop due to re-registering our // mutation observer (which would notify us again for *this* event). nsCOMPtr event = - NS_NewRunnableMethodWithArg> + mozilla::NewRunnableMethod> (this, &nsFormFillController::MaybeStartControllingInput, focusedInput); NS_DispatchToCurrentThread(event); } diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 46a6876c7e..b5d4012f67 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -1748,7 +1748,7 @@ public: ReadLastShutdownDuration(mShutdownTimeFilename); mTelemetry->ReadLateWritesStacks(mProfileDir); nsCOMPtr e = - NS_NewRunnableMethod(this, &nsFetchTelemetryData::MainThread); + NewRunnableMethod(this, &nsFetchTelemetryData::MainThread); NS_ENSURE_STATE(e); NS_DispatchToMainThread(e); return NS_OK; diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp index 96fdfea720..f0be4f2000 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp @@ -9,6 +9,7 @@ #include "mozilla/SyncRunnable.h" using namespace mozilla::safebrowsing; +using mozilla::NewRunnableMethod; static nsresult DispatchToWorkerThread(nsIRunnable* r) @@ -113,8 +114,8 @@ NS_IMETHODIMP UrlClassifierDBServiceWorkerProxy::FinishStream() { nsCOMPtr r = - NS_NewRunnableMethod(mTarget, - &nsIUrlClassifierDBServiceWorker::FinishStream); + NewRunnableMethod(mTarget, + &nsIUrlClassifierDBServiceWorker::FinishStream); return DispatchToWorkerThread(r); } @@ -147,8 +148,8 @@ NS_IMETHODIMP UrlClassifierDBServiceWorkerProxy::FinishUpdate() { nsCOMPtr r = - NS_NewRunnableMethod(mTarget, - &nsIUrlClassifierDBServiceWorker::FinishUpdate); + NewRunnableMethod(mTarget, + &nsIUrlClassifierDBServiceWorker::FinishUpdate); return DispatchToWorkerThread(r); } @@ -156,8 +157,8 @@ NS_IMETHODIMP UrlClassifierDBServiceWorkerProxy::CancelUpdate() { nsCOMPtr r = - NS_NewRunnableMethod(mTarget, - &nsIUrlClassifierDBServiceWorker::CancelUpdate); + NewRunnableMethod(mTarget, + &nsIUrlClassifierDBServiceWorker::CancelUpdate); return DispatchToWorkerThread(r); } @@ -165,8 +166,8 @@ NS_IMETHODIMP UrlClassifierDBServiceWorkerProxy::ResetDatabase() { nsCOMPtr r = - NS_NewRunnableMethod(mTarget, - &nsIUrlClassifierDBServiceWorker::ResetDatabase); + NewRunnableMethod(mTarget, + &nsIUrlClassifierDBServiceWorker::ResetDatabase); return DispatchToWorkerThread(r); } @@ -174,8 +175,8 @@ NS_IMETHODIMP UrlClassifierDBServiceWorkerProxy::OpenDb() { nsCOMPtr r = - NS_NewRunnableMethod(mTarget, - &nsIUrlClassifierDBServiceWorker::OpenDb); + NewRunnableMethod(mTarget, + &nsIUrlClassifierDBServiceWorker::OpenDb); return DispatchToWorkerThread(r); } @@ -183,8 +184,8 @@ NS_IMETHODIMP UrlClassifierDBServiceWorkerProxy::CloseDb() { nsCOMPtr r = - NS_NewRunnableMethod(mTarget, - &nsIUrlClassifierDBServiceWorker::CloseDb); + NewRunnableMethod(mTarget, + &nsIUrlClassifierDBServiceWorker::CloseDb); return DispatchToWorkerThread(r); } diff --git a/toolkit/content/browser-content.js b/toolkit/content/browser-content.js index 53cc4c4cfe..00a6d593fa 100644 --- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -717,7 +717,7 @@ var AudioPlaybackListener = { Services.obs.addObserver(this, "audio-playback", false); Services.obs.addObserver(this, "AudioFocusChanged", false); - addMessageListener("AudioPlaybackMute", this); + addMessageListener("AudioPlayback", this); addEventListener("unload", () => { AudioPlaybackListener.uninit(); }); @@ -727,7 +727,45 @@ var AudioPlaybackListener = { Services.obs.removeObserver(this, "audio-playback"); Services.obs.removeObserver(this, "AudioFocusChanged"); - removeMessageListener("AudioPlaybackMute", this); + removeMessageListener("AudioPlayback", this); + }, + + handleMediaControlMessage(msg) { + let utils = global.content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + let suspendTypes = Ci.nsISuspendedTypes; + switch (msg) { + case "mute": + utils.audioMuted = true; + break; + case "unmute": + utils.audioMuted = false; + break; + case "lostAudioFocus": + utils.mediaSuspend = suspendTypes.SUSPENDED_STOP_DISPOSABLE; + break; + case "lostAudioFocusTransiently": + utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE; + break; + case "gainAudioFocus": + utils.mediaSuspend = suspendTypes.NONE_SUSPENDED; + break; + case "mediaControlPaused": + utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE; + break; + case "mediaControlStopped": + utils.mediaSuspend = suspendTypes.SUSPENDED_STOP_DISPOSABLE; + break; + case "blockInactivePageMedia": + utils.mediaSuspend = suspendTypes.SUSPENDED_BLOCK; + break; + case "resumeMedia": + utils.mediaSuspend = suspendTypes.NONE_SUSPENDED; + break; + default: + dump("Error : wrong media control msg!\n"); + break; + } }, observe(subject, topic, data) { @@ -738,28 +776,13 @@ var AudioPlaybackListener = { sendAsyncMessage(name); } } else if (topic == "AudioFocusChanged") { - let utils = global.content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - switch (data) { - // The AudioFocus:LossTransient means the media would be resumed after - // the interruption ended, but AudioFocus:Loss doesn't. - // TODO : distinguish these types, it would be done in bug1242874. - case "Loss": - case "LossTransient": - utils.mediaSuspended = true; - break; - case "Gain": - utils.mediaSuspended = false; - break; - } + this.handleMediaControlMessage(data); } }, receiveMessage(msg) { - if (msg.name == "AudioPlaybackMute") { - let utils = global.content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - utils.audioMuted = msg.data.type === "mute"; + if (msg.name == "AudioPlayback") { + this.handleMediaControlMessage(msg.data.type); } }, }; diff --git a/toolkit/content/tests/browser/audio.ogg b/toolkit/content/tests/browser/audio.ogg index d7f6a0ccf47fbc30e936b47e3f4d2cf8f4a90a16..7f1833508ab3fd9e6d4f0241f37cc2ee5a1679e5 100644 GIT binary patch literal 14290 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 literal 16521 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

9eH6-?Re=p+l66 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 DFy$Y2 diff --git a/toolkit/content/tests/browser/browser.ini b/toolkit/content/tests/browser/browser.ini index 811c56ef11..51e1c7cb3b 100644 --- a/toolkit/content/tests/browser/browser.ini +++ b/toolkit/content/tests/browser/browser.ini @@ -34,12 +34,15 @@ support-files = file_redirect_to.html [browser_bug1170531.js] [browser_mediaPlayback.js] +tags = audiochannel support-files = file_mediaPlayback.html file_mediaPlaybackFrame.html audio.ogg [browser_mute.js] +tags = audiochannel [browser_mute2.js] +tags = audiochannel [browser_quickfind_editable.js] [browser_saveImageURL.js] support-files = diff --git a/toolkit/content/tests/browser/browser_mediaPlayback_suspended.js b/toolkit/content/tests/browser/browser_mediaPlayback_suspended.js new file mode 100644 index 0000000000..6e659fe73b --- /dev/null +++ b/toolkit/content/tests/browser/browser_mediaPlayback_suspended.js @@ -0,0 +1,164 @@ +const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_mediaPlayback2.html"; + +var SuspendedType = { + NONE_SUSPENDED : 0, + SUSPENDED_PAUSE : 1, + SUSPENDED_BLOCK : 2, + SUSPENDED_PAUSE_DISPOSABLE : 3 +}; + +function wait_for_event(browser, event) { + return BrowserTestUtils.waitForEvent(browser, event, false, (event) => { + is(event.originalTarget, browser, "Event must be dispatched to correct browser."); + return true; + }); +} + +function check_audio_suspended(suspendedType) { + var list = content.document.getElementsByTagName('audio'); + if (list.length != 1) { + ok(false, "There should be only one audio element in page!") + } + + var audio = list[0]; + is(audio.computedSuspended, suspendedType, + "The suspended state of MediaElement is correct."); +} + +function check_audio_pause_state(expectedPauseState) { + var list = content.document.getElementsByTagName('audio'); + if (list.length != 1) { + ok(false, "There should be only one audio element in page!") + } + + var audio = list[0]; + if (expectedPauseState) { + is(audio.paused, true, "Audio is paused correctly."); + } else { + is(audio.paused, false, "Audio is resumed correctly."); + } +} + +function* suspended_pause(url, browser) { + info("### Start test for suspended-pause ###"); + browser.loadURI(url); + + info("- page should have playing audio -"); + yield wait_for_event(browser, "DOMAudioPlaybackStarted"); + + info("- the suspended state of audio should be non-suspened -"); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); + + info("- pause playing audio -"); + browser.pauseMedia(false /* non-disposable */); + yield ContentTask.spawn(browser, true /* expect for pause */, + check_audio_pause_state); + yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE, + check_audio_suspended); + + info("- resume paused audio -"); + browser.resumeMedia(); + yield ContentTask.spawn(browser, false /* expect for playing */, + check_audio_pause_state); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); +} + +function* suspended_pause_disposable(url, browser) { + info("### Start test for suspended-pause-disposable ###"); + browser.loadURI(url); + + info("- page should have playing audio -"); + yield wait_for_event(browser, "DOMAudioPlaybackStarted"); + + info("- the suspended state of audio should be non-suspened -"); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); + + info("- pause playing audio -"); + browser.pauseMedia(true /* disposable */); + yield ContentTask.spawn(browser, true /* expect for pause */, + check_audio_pause_state); + yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE_DISPOSABLE, + check_audio_suspended); + + info("- resume paused audio -"); + browser.resumeMedia(); + yield ContentTask.spawn(browser, false /* expect for playing */, + check_audio_pause_state); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); +} + +function* suspended_stop_disposable(url, browser) { + info("### Start test for suspended-stop-disposable ###"); + browser.loadURI(url); + + info("- page should have playing audio -"); + yield wait_for_event(browser, "DOMAudioPlaybackStarted"); + + info("- the suspended state of audio should be non-suspened -"); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); + + info("- stop playing audio -"); + browser.stopMedia(); + yield wait_for_event(browser, "DOMAudioPlaybackStopped"); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); +} + +function* suspended_block(url, browser) { + info("### Start test for suspended-block ###"); + browser.loadURI(url); + + info("- page should have playing audio -"); + yield wait_for_event(browser, "DOMAudioPlaybackStarted"); + + info("- block playing audio -"); + browser.blockMedia(); + yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_BLOCK, + check_audio_suspended); + + info("- resume blocked audio -"); + browser.resumeMedia(); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_audio_suspended); +} + +add_task(function* setup_test_preference() { + yield new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": [ + ["media.useAudioChannelService.testing", true] + ]}, resolve); + }); +}); + +add_task(function* test_suspended_pause() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank" + }, suspended_pause.bind(this, PAGE)); +}); + +add_task(function* test_suspended_pause_disposable() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank" + }, suspended_pause_disposable.bind(this, PAGE)); +}); + +add_task(function* test_suspended_stop_disposable() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank" + }, suspended_stop_disposable.bind(this, PAGE)); +}); + +add_task(function* test_suspended_block() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank" + }, suspended_block.bind(this, PAGE)); +}); diff --git a/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js b/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js new file mode 100644 index 0000000000..9da63b385f --- /dev/null +++ b/toolkit/content/tests/browser/browser_mediaPlayback_suspended_multipleAudio.js @@ -0,0 +1,305 @@ +const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multipleAudio.html"; + +var SuspendedType = { + NONE_SUSPENDED : 0, + SUSPENDED_PAUSE : 1, + SUSPENDED_BLOCK : 2, + SUSPENDED_PAUSE_DISPOSABLE : 3 +}; + +function wait_for_event(browser, event) { + return BrowserTestUtils.waitForEvent(browser, event, false, (event) => { + is(event.originalTarget, browser, "Event must be dispatched to correct browser."); + return true; + }); +} + +function check_all_audio_suspended(suspendedType) { + var autoPlay = content.document.getElementById('autoplay'); + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!autoPlay || !nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(autoPlay.computedSuspended, suspendedType, + "The suspeded state of autoplay audio is correct."); + is(nonAutoPlay.computedSuspended, suspendedType, + "The suspeded state of non-autoplay audio is correct."); +} + +function check_autoplay_audio_suspended(suspendedType) { + var autoPlay = content.document.getElementById('autoplay'); + if (!autoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(autoPlay.computedSuspended, suspendedType, + "The suspeded state of autoplay audio is correct."); +} + +function check_nonautoplay_audio_suspended(suspendedType) { + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(nonAutoPlay.computedSuspended, suspendedType, + "The suspeded state of non-autoplay audio is correct."); +} + +function check_autoplay_audio_pause_state(expectedPauseState) { + var autoPlay = content.document.getElementById('autoplay'); + if (!autoPlay) { + ok(false, "Can't get the audio element!"); + } + + if (expectedPauseState) { + is(autoPlay.paused, true, "Audio is paused correctly."); + } else { + is(autoPlay.paused, false, "Audio is resumed correctly."); + } +} + +function play_nonautoplay_audio_should_be_paused() { + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + nonAutoPlay.play(); + return new Promise(resolve => { + nonAutoPlay.onpause = function () { + nonAutoPlay.onpause = null; + is(nonAutoPlay.ended, false, "Audio can't be playback."); + resolve(); + } + }); +} + +function all_audio_onresume() { + var autoPlay = content.document.getElementById('autoplay'); + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!autoPlay || !nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(autoPlay.paused, false, "Autoplay audio is resumed."); + is(nonAutoPlay.paused, false, "Non-AutoPlay audio is resumed."); +} + +function all_audio_onpause() { + var autoPlay = content.document.getElementById('autoplay'); + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!autoPlay || !nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(autoPlay.paused, true, "Autoplay audio is paused."); + is(nonAutoPlay.paused, true, "Non-AutoPlay audio is paused."); +} + +function play_nonautoplay_audio_should_play_until_ended() { + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + nonAutoPlay.play(); + return new Promise(resolve => { + nonAutoPlay.onended = function () { + nonAutoPlay.onended = null; + ok(true, "Audio can be playback until ended."); + resolve(); + } + }); +} + +function no_audio_resumed() { + var autoPlay = content.document.getElementById('autoplay'); + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!autoPlay || !nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(autoPlay.paused && nonAutoPlay.paused, true, "No audio was resumed."); +} + +function play_nonautoplay_audio_should_be_blocked(suspendedType) { + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + nonAutoPlay.play(); + return new Promise(resolve => { + nonAutoPlay.onplay = function () { + nonAutoPlay.onplay = null; + is(nonAutoPlay.computedSuspended, suspendedType, + "The suspeded state of non-autoplay audio is correct."); + resolve(); + } + }); +} + +function* suspended_pause(url, browser) { + info("### Start test for suspended-pause ###"); + browser.loadURI(url); + + info("- page should have playing audio -"); + yield wait_for_event(browser, "DOMAudioPlaybackStarted"); + + info("- the default suspended state of all audio should be non-suspened-"); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); + + info("- pause all audio in the page -"); + browser.pauseMedia(false /* non-disposable */); + yield ContentTask.spawn(browser, true /* expect for pause */, + check_autoplay_audio_pause_state); + yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE, + check_autoplay_audio_suspended); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_nonautoplay_audio_suspended); + + info("- no audio can be playback during suspended-paused -"); + yield ContentTask.spawn(browser, null, + play_nonautoplay_audio_should_be_paused); + yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE, + check_nonautoplay_audio_suspended); + + info("- both audio should be resumed at the same time -"); + browser.resumeMedia(); + yield ContentTask.spawn(browser, null, + all_audio_onresume); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); + + info("- both audio should be paused at the same time -"); + browser.pauseMedia(false /* non-disposable */); + yield ContentTask.spawn(browser, null, all_audio_onpause); +} + +function* suspended_pause_disposable(url, browser) { + info("### Start test for suspended-pause-disposable ###"); + browser.loadURI(url); + + info("- page should have playing audio -"); + yield wait_for_event(browser, "DOMAudioPlaybackStarted"); + + info("- the default suspended state of all audio should be non-suspened -"); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); + + info("- only pause playing audio in the page -"); + browser.pauseMedia(true /* non-disposable */); + yield ContentTask.spawn(browser, true /* expect for pause */, + check_autoplay_audio_pause_state); + yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE_DISPOSABLE, + check_autoplay_audio_suspended); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_nonautoplay_audio_suspended); + + info("- new playing audio should be playback correctly -"); + yield ContentTask.spawn(browser, null, + play_nonautoplay_audio_should_play_until_ended); + + info("- should only resume one audio -"); + browser.resumeMedia(); + yield ContentTask.spawn(browser, false /* expect for playing */, + check_autoplay_audio_pause_state); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); +} + +function* suspended_stop_disposable(url, browser) { + info("### Start test for suspended-stop-disposable ###"); + browser.loadURI(url); + + info("- page should have playing audio -"); + yield wait_for_event(browser, "DOMAudioPlaybackStarted"); + + info("- the default suspended state of all audio should be non-suspened -"); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); + + info("- only stop playing audio in the page -"); + browser.stopMedia(); + yield ContentTask.spawn(browser, true /* expect for pause */, + check_autoplay_audio_pause_state); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); + + info("- new playing audio should be playback correctly -"); + yield ContentTask.spawn(browser, null, + play_nonautoplay_audio_should_play_until_ended); + + info("- no any audio can be resumed by page -"); + browser.resumeMedia(); + yield ContentTask.spawn(browser, null, no_audio_resumed); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); +} + +function* suspended_block(url, browser) { + info("### Start test for suspended-block ###"); + browser.loadURI(url); + + info("- page should have playing audio -"); + yield wait_for_event(browser, "DOMAudioPlaybackStarted"); + + info("- the default suspended state of all audio should be non-suspened-"); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); + + info("- block autoplay audio -"); + browser.blockMedia(); + yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_BLOCK, + check_autoplay_audio_suspended); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_nonautoplay_audio_suspended); + + info("- no audio can be playback during suspended-block -"); + yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_BLOCK, + play_nonautoplay_audio_should_be_blocked); + + info("- both audio should be resumed at the same time -"); + browser.resumeMedia(); + yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED, + check_all_audio_suspended); +} + +add_task(function* setup_test_preference() { + yield new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": [ + ["media.useAudioChannelService.testing", true] + ]}, resolve); + }); +}); + +add_task(function* test_suspended_pause() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank" + }, suspended_pause.bind(this, PAGE)); +}); + +add_task(function* test_suspended_pause_disposable() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank" + }, suspended_pause_disposable.bind(this, PAGE)); +}); + +add_task(function* test_suspended_stop_disposable() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank" + }, suspended_stop_disposable.bind(this, PAGE)); +}); + +add_task(function* test_suspended_block() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank" + }, suspended_block.bind(this, PAGE)); +}); diff --git a/toolkit/content/tests/browser/file_multipleAudio.html b/toolkit/content/tests/browser/file_multipleAudio.html new file mode 100644 index 0000000000..fb860aac19 --- /dev/null +++ b/toolkit/content/tests/browser/file_multipleAudio.html @@ -0,0 +1,7 @@ + + + + + + diff --git a/toolkit/content/tests/widgets/audio.ogg b/toolkit/content/tests/widgets/audio.ogg index 7e6ef77ec407eccb3e07bc21d85d8418ab84e011..a553c23e73faf08c8fb55760e82de265dc43f6de 100644 GIT binary patch literal 14293 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 L8KN)BbN&ASz3Fi! diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml index b1c45f96fd..4f983b207c 100644 --- a/toolkit/content/widgets/browser.xml +++ b/toolkit/content/widgets/browser.xml @@ -739,7 +739,7 @@ @@ -749,12 +749,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Run(); } -template<> -struct RunnableMethodTraits -{ - static void RetainCallee(ContentChild* obj) { } - static void ReleaseCallee(ContentChild* obj) { } -}; - void XRE_ShutdownChildProcess() { diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp index 83002ca022..cc8b87ef63 100644 --- a/toolkit/xre/nsUpdateDriver.cpp +++ b/toolkit/xre/nsUpdateDriver.cpp @@ -1204,8 +1204,8 @@ nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate) #endif MOZ_ASSERT(NS_IsMainThread(), "not main thread"); - return NS_NewThread(getter_AddRefs(mProcessWatcher), - NS_NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate)); + nsCOMPtr r = NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate); + return NS_NewThread(getter_AddRefs(mProcessWatcher), r); } @@ -1229,13 +1229,13 @@ nsUpdateProcessor::StartStagedUpdate() if (mUpdaterPID) { // Track the state of the updater process while it is staging an update. - rv = NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess)); + rv = NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess)); NS_ENSURE_SUCCESS_VOID(rv); } else { // Failed to launch the updater process for some reason. // We need to shutdown the current thread as there isn't anything more for // us to do... - rv = NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread)); + rv = NS_DispatchToMainThread(NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread)); NS_ENSURE_SUCCESS_VOID(rv); } } @@ -1253,7 +1253,7 @@ nsUpdateProcessor::WaitForProcess() { MOZ_ASSERT(!NS_IsMainThread(), "main thread"); ::WaitForProcess(mUpdaterPID); - NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone)); + NS_DispatchToMainThread(NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone)); } void diff --git a/uriloader/prefetch/nsOfflineCacheUpdate.cpp b/uriloader/prefetch/nsOfflineCacheUpdate.cpp index 1598520a30..ad1e88c04a 100644 --- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp +++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp @@ -1736,10 +1736,8 @@ nsOfflineCacheUpdate::Begin() mItemsInProgress = 0; if (mState == STATE_CANCELLED) { - RefPtr > errorNotification = - NS_NewRunnableMethod(this, - &nsOfflineCacheUpdate::AsyncFinishWithError); - nsresult rv = NS_DispatchToMainThread(errorNotification); + nsresult rv = NS_DispatchToMainThread(NewRunnableMethod(this, + &nsOfflineCacheUpdate::AsyncFinishWithError)); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; diff --git a/widget/CompositorWidgetProxy.cpp b/widget/CompositorWidgetProxy.cpp index a4594a2b70..3d58854ce3 100644 --- a/widget/CompositorWidgetProxy.cpp +++ b/widget/CompositorWidgetProxy.cpp @@ -5,6 +5,7 @@ #include "CompositorWidgetProxy.h" #include "GLConsts.h" #include "nsBaseWidget.h" +#include "VsyncDispatcher.h" namespace mozilla { namespace widget { @@ -69,18 +70,12 @@ CompositorWidgetProxyWrapper::CompositorWidgetProxyWrapper(nsBaseWidget* aWidget bool CompositorWidgetProxyWrapper::PreRender(layers::LayerManagerComposite* aManager) { - if (!mWidget) { - return false; - } return mWidget->PreRender(aManager); } void CompositorWidgetProxyWrapper::PostRender(layers::LayerManagerComposite* aManager) { - if (!mWidget) { - return; - } mWidget->PostRender(aManager); } @@ -88,9 +83,6 @@ void CompositorWidgetProxyWrapper::DrawWindowUnderlay(layers::LayerManagerComposite* aManager, LayoutDeviceIntRect aRect) { - if (!mWidget) { - return; - } mWidget->DrawWindowUnderlay(aManager, aRect); } @@ -98,18 +90,12 @@ void CompositorWidgetProxyWrapper::DrawWindowOverlay(layers::LayerManagerComposite* aManager, LayoutDeviceIntRect aRect) { - if (!mWidget) { - return; - } mWidget->DrawWindowOverlay(aManager, aRect); } already_AddRefed CompositorWidgetProxyWrapper::StartRemoteDrawing() { - if (!mWidget) { - return nullptr; - } return mWidget->StartRemoteDrawing(); } @@ -117,18 +103,12 @@ already_AddRefed CompositorWidgetProxyWrapper::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, layers::BufferMode* aBufferMode) { - if (!mWidget) { - return nullptr; - } return mWidget->StartRemoteDrawingInRegion(aInvalidRegion, aBufferMode); } void CompositorWidgetProxyWrapper::EndRemoteDrawing() { - if (!mWidget) { - return; - } mWidget->EndRemoteDrawing(); } @@ -136,71 +116,63 @@ void CompositorWidgetProxyWrapper::EndRemoteDrawingInRegion(gfx::DrawTarget* aDrawTarget, LayoutDeviceIntRegion& aInvalidRegion) { - if (!mWidget) { - return; - } mWidget->EndRemoteDrawingInRegion(aDrawTarget, aInvalidRegion); } void CompositorWidgetProxyWrapper::CleanupRemoteDrawing() { - if (mWidget) { - mWidget->CleanupRemoteDrawing(); - } - CompositorWidgetProxy::CleanupRemoteDrawing(); + mWidget->CleanupRemoteDrawing(); } void CompositorWidgetProxyWrapper::CleanupWindowEffects() { - if (!mWidget) { - return; - } mWidget->CleanupWindowEffects(); } bool CompositorWidgetProxyWrapper::InitCompositor(layers::Compositor* aCompositor) { - if (!mWidget) { - return false; - } return mWidget->InitCompositor(aCompositor); } LayoutDeviceIntSize CompositorWidgetProxyWrapper::GetClientSize() { - if (!mWidget) { - return LayoutDeviceIntSize(); - } return mWidget->GetClientSize(); } uint32_t CompositorWidgetProxyWrapper::GetGLFrameBufferFormat() { - if (!mWidget) { - return CompositorWidgetProxy::GetGLFrameBufferFormat(); - } return mWidget->GetGLFrameBufferFormat(); } layers::Composer2D* CompositorWidgetProxyWrapper::GetComposer2D() { - if (!mWidget) { - return nullptr; - } return mWidget->GetComposer2D(); } +uintptr_t +CompositorWidgetProxyWrapper::GetWidgetKey() +{ + return reinterpret_cast(mWidget); +} + nsIWidget* CompositorWidgetProxyWrapper::RealWidget() { return mWidget; } +already_AddRefed +CompositorWidgetProxyWrapper::GetCompositorVsyncDispatcher() +{ + RefPtr cvd = mWidget->GetCompositorVsyncDispatcher(); + return cvd.forget(); +} + } // namespace widget } // namespace mozilla diff --git a/widget/CompositorWidgetProxy.h b/widget/CompositorWidgetProxy.h index eb5eea4073..225a03a4f4 100644 --- a/widget/CompositorWidgetProxy.h +++ b/widget/CompositorWidgetProxy.h @@ -15,6 +15,7 @@ class nsIWidget; class nsBaseWidget; namespace mozilla { +class CompositorVsyncDispatcher; namespace layers { class Compositor; class LayerManagerComposite; @@ -26,6 +27,8 @@ class DrawTarget; } // namespace gfx namespace widget { +class WinCompositorWidgetProxy; + /** * Access to a widget from the compositor is restricted to these methods. */ @@ -162,6 +165,19 @@ public: */ virtual void CleanupRemoteDrawing(); + /** + * Return a key that can represent the widget object round-trip across the + * CompositorBridge channel. This only needs to be implemented on GTK and + * Windows. + * + * The key must be the nsIWidget pointer cast to a uintptr_t. See + * CompositorBridgeChild::RecvHideAllPlugins and + * CompositorBridgeParent::SendHideAllPlugins. + */ + virtual uintptr_t GetWidgetKey() { + return 0; + } + /** * Create a backbuffer for the software compositor. */ @@ -170,6 +186,15 @@ public: const LayoutDeviceIntRect& aRect, const LayoutDeviceIntRect& aClearRect); + /** + * Return a compositor vsync dispatcher for this widget. + */ + virtual already_AddRefed GetCompositorVsyncDispatcher() = 0; + + virtual WinCompositorWidgetProxy* AsWindowsProxy() { + return nullptr; + } + protected: virtual ~CompositorWidgetProxy(); @@ -204,6 +229,8 @@ public: virtual LayoutDeviceIntSize GetClientSize() override; virtual uint32_t GetGLFrameBufferFormat() override; virtual layers::Composer2D* GetComposer2D() override; + virtual already_AddRefed GetCompositorVsyncDispatcher() override; + virtual uintptr_t GetWidgetKey() override; // If you can override this method, inherit from CompositorWidgetProxy instead. nsIWidget* RealWidget() override; diff --git a/widget/ScreenProxy.cpp b/widget/ScreenProxy.cpp index cf774a6e9a..5257eb68ab 100644 --- a/widget/ScreenProxy.cpp +++ b/widget/ScreenProxy.cpp @@ -174,9 +174,7 @@ ScreenProxy::InvalidateCacheOnNextTick() mCacheWillInvalidate = true; - nsCOMPtr r = - NS_NewRunnableMethod(this, &ScreenProxy::InvalidateCache); - nsContentUtils::RunInStableState(r.forget()); + nsContentUtils::RunInStableState(NewRunnableMethod(this, &ScreenProxy::InvalidateCache)); } void diff --git a/widget/VsyncDispatcher.cpp b/widget/VsyncDispatcher.cpp index b57c71136d..04ad7410b1 100644 --- a/widget/VsyncDispatcher.cpp +++ b/widget/VsyncDispatcher.cpp @@ -89,7 +89,7 @@ CompositorVsyncDispatcher::SetCompositorVsyncObserver(VsyncObserver* aVsyncObser } bool observeVsync = aVsyncObserver != nullptr; - nsCOMPtr vsyncControl = NS_NewRunnableMethodWithArg(this, + nsCOMPtr vsyncControl = NewRunnableMethod(this, &CompositorVsyncDispatcher::ObserveVsync, observeVsync); NS_DispatchToMainThread(vsyncControl); @@ -180,9 +180,8 @@ void RefreshTimerVsyncDispatcher::UpdateVsyncStatus() { if (!NS_IsMainThread()) { - nsCOMPtr vsyncControl = NS_NewRunnableMethod(this, - &RefreshTimerVsyncDispatcher::UpdateVsyncStatus); - NS_DispatchToMainThread(vsyncControl); + NS_DispatchToMainThread(NewRunnableMethod(this, + &RefreshTimerVsyncDispatcher::UpdateVsyncStatus)); return; } diff --git a/widget/gonk/nsWindow.cpp b/widget/gonk/nsWindow.cpp index c2b43fc0bc..526d6fd551 100644 --- a/widget/gonk/nsWindow.cpp +++ b/widget/gonk/nsWindow.cpp @@ -44,6 +44,7 @@ #include "mozilla/layers/APZCTreeManager.h" #include "mozilla/layers/APZThreadUtils.h" #include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorSession.h" #include "mozilla/TouchEvents.h" #include "HwcComposer2D.h" @@ -697,10 +698,10 @@ nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, } CreateCompositor(); - if (mCompositorBridgeParent) { - mScreen->SetCompositorBridgeParent(mCompositorBridgeParent); + if (RefPtr bridge = GetCompositorBridgeParent()) { + mScreen->SetCompositorBridgeParent(bridge); if (mScreen->IsPrimaryScreen()) { - mComposer2D->SetCompositorBridgeParent(mCompositorBridgeParent); + mComposer2D->SetCompositorBridgeParent(bridge); } } MOZ_ASSERT(mLayerManager); @@ -710,7 +711,7 @@ nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, void nsWindow::DestroyCompositor() { - if (mCompositorBridgeParent) { + if (RefPtr bridge = GetCompositorBridgeParent()) { mScreen->SetCompositorBridgeParent(nullptr); if (mScreen->IsPrimaryScreen()) { // Unset CompositorBridgeParent @@ -720,12 +721,6 @@ nsWindow::DestroyCompositor() nsBaseWidget::DestroyCompositor(); } -CompositorBridgeParent* -nsWindow::NewCompositorBridgeParent(int aSurfaceWidth, int aSurfaceHeight) -{ - return new CompositorBridgeParent(this, true, aSurfaceWidth, aSurfaceHeight); -} - void nsWindow::BringToTop() { @@ -799,3 +794,9 @@ nsWindow::GetComposer2D() return mComposer2D; } + +CompositorBridgeParent* +nsWindow::GetCompositorBridgeParent() const +{ + return mCompositorSession ? mCompositorSession->GetInProcessBridge() : nullptr; +} diff --git a/widget/gonk/nsWindow.h b/widget/gonk/nsWindow.h index 74cecc66b6..3460d15458 100644 --- a/widget/gonk/nsWindow.h +++ b/widget/gonk/nsWindow.h @@ -117,8 +117,6 @@ public: bool* aAllowRetaining = nullptr); virtual void DestroyCompositor(); - virtual CompositorBridgeParent* NewCompositorBridgeParent(int aSurfaceWidth, int aSurfaceHeight); - NS_IMETHOD_(void) SetInputContext(const InputContext& aContext, const InputContextAction& aAction); NS_IMETHOD_(InputContext) GetInputContext(); @@ -148,6 +146,11 @@ protected: // event (like a keypress or mouse click). void UserActivity(); + bool UseExternalCompositingSurface() const override { + return true; + } + CompositorBridgeParent* GetCompositorBridgeParent() const; + private: // This is used by SynthesizeNativeTouchPoint to maintain state between // multiple synthesized points diff --git a/widget/gtk/nsDeviceContextSpecG.cpp b/widget/gtk/nsDeviceContextSpecG.cpp index 0c69a907af..dbbddebb13 100644 --- a/widget/gtk/nsDeviceContextSpecG.cpp +++ b/widget/gtk/nsDeviceContextSpecG.cpp @@ -269,9 +269,7 @@ gboolean nsDeviceContextSpecGTK::PrinterEnumerator(GtkPrinter *aPrinter, // misunderstanding what the capabilities of the printer are due to a // GTK bug (https://bugzilla.gnome.org/show_bug.cgi?id=753041). We // sidestep this by deferring the print to the next tick. - nsCOMPtr event = - NS_NewRunnableMethod(spec, &nsDeviceContextSpecGTK::StartPrintJob); - NS_DispatchToCurrentThread(event); + NS_DispatchToCurrentThread(NewRunnableMethod(spec, &nsDeviceContextSpecGTK::StartPrintJob)); return TRUE; } } @@ -329,9 +327,7 @@ NS_IMETHODIMP nsDeviceContextSpecGTK::EndDocument() } else { // We don't have a printer. We have to enumerate the printers and find // one with a matching name. - nsCOMPtr event = - NS_NewRunnableMethod(this, &nsDeviceContextSpecGTK::EnumeratePrinters); - NS_DispatchToCurrentThread(event); + NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsDeviceContextSpecGTK::EnumeratePrinters)); } } else { // Handle print-to-file ourselves for the benefit of embedders diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index cf8b86b758..28cd86456d 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -2144,7 +2144,7 @@ nsWindow::OnExposeEvent(cairo_t *cr) ? static_cast(GetLayerManager()) : nullptr; - if (clientLayers && mCompositorBridgeParent) { + if (clientLayers && mCompositorSession) { // We need to paint to the screen even if nothing changed, since if we // don't have a compositing window manager, our pixels could be stale. clientLayers->SetNeedsComposite(true); @@ -2169,9 +2169,9 @@ nsWindow::OnExposeEvent(cairo_t *cr) return FALSE; } - if (clientLayers && mCompositorBridgeParent && clientLayers->NeedsComposite()) { - mCompositorBridgeParent->ScheduleRenderOnCompositorThread(); - clientLayers->SetNeedsComposite(false); + if (clientLayers && clientLayers->NeedsComposite()) { + clientLayers->Composite(); + clientLayers->SetNeedsComposite(false); } LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n", @@ -2473,9 +2473,7 @@ nsWindow::OnSizeAllocate(GtkAllocation *aAllocation) // GtkWindow callers of gtk_widget_size_allocate expect the signal // handlers to return sometime in the near future. mNeedsDispatchResized = true; - nsCOMPtr r = - NS_NewRunnableMethod(this, &nsWindow::MaybeDispatchResized); - NS_DispatchToCurrentThread(r.forget()); + NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsWindow::MaybeDispatchResized)); } void @@ -4628,8 +4626,12 @@ nsWindow::GrabPointer(guint32 aTime) LOG(("GrabPointer: pointer grab failed: %i\n", retval)); // A failed grab indicates that another app has grabbed the pointer. // Check for rollup now, because, without the grab, we likely won't - // get subsequent button press events. - CheckForRollup(0, 0, false, true); + // get subsequent button press events. Do this with an event so that + // popups don't rollup while potentially adjusting the grab for + // this popup. + nsCOMPtr event = + NewRunnableMethod(this, &nsWindow::CheckForRollupDuringGrab); + NS_DispatchToCurrentThread(event.forget()); } } diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index f24e1cdf14..ecc4c75ee2 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -421,6 +421,11 @@ private: bool DispatchContentCommandEvent(mozilla::EventMessage aMsg); bool CheckForRollup(gdouble aMouseX, gdouble aMouseY, bool aIsWheel, bool aAlwaysRollup); + void CheckForRollupDuringGrab() + { + CheckForRollup(0, 0, false, true); + } + bool GetDragInfo(mozilla::WidgetMouseEvent* aMouseEvent, GdkWindow** aWindow, gint* aButton, gint* aRootX, gint* aRootY); diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 4f78b1b2ac..08fab47eeb 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -70,6 +70,7 @@ #include "nsAccessibilityService.h" #endif #include "gfxConfig.h" +#include "mozilla/layers/CompositorSession.h" #ifdef DEBUG #include "nsIObserver.h" @@ -271,8 +272,8 @@ void nsBaseWidget::DestroyCompositor() // XXX CompositorBridgeChild and CompositorBridgeParent might be re-created in // ClientLayerManager destructor. See bug 1133426. RefPtr compositorChild = mCompositorBridgeChild; - RefPtr compositorParent = mCompositorBridgeParent; - mCompositorBridgeChild->Destroy(); + RefPtr compositorSession = mCompositorSession; + mCompositorSession->Shutdown(); mCompositorBridgeChild = nullptr; } @@ -296,7 +297,7 @@ void nsBaseWidget::DestroyLayerManager() void nsBaseWidget::OnRenderingDeviceReset() { - if (!mLayerManager || !mCompositorBridgeParent) { + if (!mLayerManager || !mCompositorSession) { return; } @@ -316,9 +317,14 @@ nsBaseWidget::OnRenderingDeviceReset() return; } + RefPtr parent = mCompositorSession->GetInProcessBridge(); + if (!parent) { + return; + } + // Recreate the compositor. TextureFactoryIdentifier identifier; - if (!mCompositorBridgeParent->ResetCompositor(backendHints, &identifier)) { + if (!parent->ResetCompositor(backendHints, &identifier)) { // No action was taken, so we don't have to do anything. return; } @@ -937,20 +943,6 @@ nsBaseWidget::UseAPZ() (WindowType() == eWindowType_toplevel || WindowType() == eWindowType_child)); } -CompositorBridgeParent* -nsBaseWidget::NewCompositorBridgeParent(int aSurfaceWidth, - int aSurfaceHeight) -{ - if (!mCompositorWidgetProxy) { - mCompositorWidgetProxy = NewCompositorWidgetProxy(); - } - return new CompositorBridgeParent(mCompositorWidgetProxy, - GetDefaultScale(), - UseAPZ(), - false, - aSurfaceWidth, aSurfaceHeight); -} - void nsBaseWidget::CreateCompositor() { LayoutDeviceIntRect rect; @@ -981,9 +973,11 @@ void nsBaseWidget::ConfigureAPZCTreeManager() bool aPreventDefault) { MOZ_ASSERT(NS_IsMainThread()); - APZThreadUtils::RunOnControllerThread(NewRunnableMethod( - treeManager.get(), &APZCTreeManager::ContentReceivedInputBlock, - aInputBlockId, aPreventDefault)); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + (treeManager, + &APZCTreeManager::ContentReceivedInputBlock, + aInputBlockId, + aPreventDefault)); }); mAPZEventState = new APZEventState(this, mozilla::Move(callback)); @@ -991,15 +985,16 @@ void nsBaseWidget::ConfigureAPZCTreeManager() const nsTArray& aFlags) { MOZ_ASSERT(NS_IsMainThread()); - APZThreadUtils::RunOnControllerThread(NewRunnableMethod( - treeManager.get(), &APZCTreeManager::SetAllowedTouchBehavior, - aInputBlockId, aFlags)); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + >>(treeManager, + &APZCTreeManager::SetAllowedTouchBehavior, + aInputBlockId, aFlags)); }; mRootContentController = CreateRootContentController(); if (mRootContentController) { - uint64_t rootLayerTreeId = mCompositorBridgeParent->RootLayerTreeId(); - CompositorBridgeParent::SetControllerForLayerTree(rootLayerTreeId, mRootContentController); + mCompositorSession->SetContentController(mRootContentController); } // When APZ is enabled, we can actually enable raw touch events because we @@ -1024,8 +1019,10 @@ nsBaseWidget::SetConfirmedTargetAPZC(uint64_t aInputBlockId, // Need to specifically bind this since it's overloaded. void (APZCTreeManager::*setTargetApzcFunc)(uint64_t, const nsTArray&) = &APZCTreeManager::SetTargetAPZC; - APZThreadUtils::RunOnControllerThread(NewRunnableMethod( - mAPZC.get(), setTargetApzcFunc, aInputBlockId, aTargets)); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + >>(mAPZC, + setTargetApzcFunc, + aInputBlockId, aTargets)); } void @@ -1033,7 +1030,7 @@ nsBaseWidget::UpdateZoomConstraints(const uint32_t& aPresShellId, const FrameMetrics::ViewID& aViewId, const Maybe& aConstraints) { - if (!mCompositorBridgeParent || !mAPZC) { + if (!mCompositorSession || !mAPZC) { if (mInitialZoomConstraints) { MOZ_ASSERT(mInitialZoomConstraints->mPresShellID == aPresShellId); MOZ_ASSERT(mInitialZoomConstraints->mViewID == aViewId); @@ -1049,7 +1046,7 @@ nsBaseWidget::UpdateZoomConstraints(const uint32_t& aPresShellId, } return; } - uint64_t layersId = mCompositorBridgeParent->RootLayerTreeId(); + uint64_t layersId = mCompositorSession->RootLayerTreeId(); mAPZC->UpdateZoomConstraints(ScrollableLayerGuid(layersId, aPresShellId, aViewId), aConstraints); } @@ -1074,7 +1071,7 @@ nsBaseWidget::ProcessUntransformedAPZEvent(WidgetInputEvent* aEvent, // event. If the event is instead targeted to an APZC in the child process, // the transform will be applied in the child process before dispatching // the event there (see e.g. TabChild::RecvRealTouchEvent()). - if (aGuid.mLayersId == mCompositorBridgeParent->RootLayerTreeId()) { + if (aGuid.mLayersId == mCompositorSession->RootLayerTreeId()) { APZCCallbackHelper::ApplyCallbackTransform(*aEvent, aGuid, GetDefaultScale()); } @@ -1263,7 +1260,7 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(), "This function assumes OMTC"); - MOZ_ASSERT(!mCompositorBridgeParent && !mCompositorBridgeChild, + MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild, "Should have properly cleaned up the previous PCompositor pair beforehand"); if (mCompositorBridgeChild) { @@ -1280,16 +1277,23 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) } CreateCompositorVsyncDispatcher(); - mCompositorBridgeParent = NewCompositorBridgeParent(aWidth, aHeight); + + if (!mCompositorWidgetProxy) { + mCompositorWidgetProxy = NewCompositorWidgetProxy(); + } + RefPtr lm = new ClientLayerManager(this); - mCompositorBridgeChild = new CompositorBridgeChild(lm); - mCompositorBridgeChild->OpenSameProcess(mCompositorBridgeParent); - // Make sure the parent knows it is same process. - mCompositorBridgeParent->SetOtherProcessId(base::GetCurrentProcId()); + mCompositorSession = CompositorSession::CreateTopLevel( + mCompositorWidgetProxy, + lm, + GetDefaultScale(), + UseAPZ(), + UseExternalCompositingSurface(), + aWidth, aHeight); + mCompositorBridgeChild = mCompositorSession->GetCompositorBridgeChild(); - uint64_t rootLayerTreeId = mCompositorBridgeParent->RootLayerTreeId(); - mAPZC = CompositorBridgeParent::GetAPZCTreeManager(rootLayerTreeId); + mAPZC = mCompositorSession->GetAPZCTreeManager(); if (mAPZC) { ConfigureAPZCTreeManager(); } @@ -1325,7 +1329,7 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) DestroyCompositor(); mLayerManager = nullptr; mCompositorBridgeChild = nullptr; - mCompositorBridgeParent = nullptr; + mCompositorSession = nullptr; mCompositorVsyncDispatcher = nullptr; return; } @@ -1859,10 +1863,10 @@ nsBaseWidget::ZoomToRect(const uint32_t& aPresShellId, const CSSRect& aRect, const uint32_t& aFlags) { - if (!mCompositorBridgeParent || !mAPZC) { + if (!mCompositorSession || !mAPZC) { return; } - uint64_t layerId = mCompositorBridgeParent->RootLayerTreeId(); + uint64_t layerId = mCompositorSession->RootLayerTreeId(); mAPZC->ZoomToRect(ScrollableLayerGuid(layerId, aPresShellId, aViewId), aRect, aFlags); } @@ -1908,13 +1912,15 @@ nsBaseWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) return; } - MOZ_ASSERT(XRE_IsParentProcess() && mCompositorBridgeParent); + MOZ_ASSERT(XRE_IsParentProcess() && mCompositorSession); - int layersId = mCompositorBridgeParent->RootLayerTreeId();; + int layersId = mCompositorSession->RootLayerTreeId();; ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId, aDragMetrics.mViewId); - APZThreadUtils::RunOnControllerThread( - NewRunnableMethod(mAPZC.get(), &APZCTreeManager::StartScrollbarDrag, guid, aDragMetrics)); + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + (mAPZC, + &APZCTreeManager::StartScrollbarDrag, + guid, aDragMetrics)); } already_AddRefed diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index 346024fdae..2fe0db4bbb 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -28,6 +28,7 @@ class nsAutoRollup; class gfxContext; namespace mozilla { +class CompositorVsyncDispatcher; #ifdef ACCESSIBILITY namespace a11y { class Accessible; @@ -45,6 +46,7 @@ class CompositorBridgeParent; class APZCTreeManager; class GeckoContentController; class APZEventState; +class CompositorSession; struct ScrollableLayerGuid; } // namespace layers @@ -111,6 +113,7 @@ protected: typedef mozilla::CSSRect CSSRect; typedef mozilla::ScreenRotation ScreenRotation; typedef mozilla::widget::CompositorWidgetProxy CompositorWidgetProxy; + typedef mozilla::layers::CompositorSession CompositorSession; virtual ~nsBaseWidget(); @@ -167,9 +170,8 @@ public: LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT, bool* aAllowRetaining = nullptr) override; - CompositorVsyncDispatcher* GetCompositorVsyncDispatcher() override; + mozilla::CompositorVsyncDispatcher* GetCompositorVsyncDispatcher(); void CreateCompositorVsyncDispatcher(); - virtual CompositorBridgeParent* NewCompositorBridgeParent(int aSurfaceWidth, int aSurfaceHeight); virtual void CreateCompositor(); virtual void CreateCompositor(int aWidth, int aHeight); virtual void PrepareWindowEffects() override {} @@ -537,6 +539,11 @@ protected: bool UseAPZ(); protected: + // Returns whether compositing should use an external surface size. + virtual bool UseExternalCompositingSurface() const { + return false; + } + /** * Starts the OMTC compositor destruction sequence. * @@ -555,8 +562,8 @@ protected: nsIWidgetListener* mAttachedWidgetListener; nsIWidgetListener* mPreviouslyAttachedWidgetListener; RefPtr mLayerManager; + RefPtr mCompositorSession; RefPtr mCompositorBridgeChild; - RefPtr mCompositorBridgeParent; RefPtr mCompositorVsyncDispatcher; RefPtr mAPZC; RefPtr mRootContentController; diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 4a66630cc1..8c5a665fa5 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -42,7 +42,6 @@ class nsIRunnable; class nsIKeyEventInPluginCallback; namespace mozilla { -class CompositorVsyncDispatcher; namespace dom { class TabChild; } // namespace dom @@ -347,7 +346,6 @@ class nsIWidget : public nsISupports typedef mozilla::widget::TextEventDispatcher TextEventDispatcher; typedef mozilla::widget::TextEventDispatcherListener TextEventDispatcherListener; - typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher; typedef mozilla::LayoutDeviceIntMargin LayoutDeviceIntMargin; typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint; typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; @@ -557,11 +555,6 @@ class nsIWidget : public nsISupports */ virtual mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() = 0; - /** - * Returns the CompositorVsyncDispatcher associated with this widget - */ - virtual CompositorVsyncDispatcher* GetCompositorVsyncDispatcher() = 0; - /** * Return the default scale factor for the window. This is the * default number of device pixels per CSS pixel to use. This should diff --git a/widget/nsScreenManagerProxy.cpp b/widget/nsScreenManagerProxy.cpp index 18661451b4..a4448b2566 100644 --- a/widget/nsScreenManagerProxy.cpp +++ b/widget/nsScreenManagerProxy.cpp @@ -199,9 +199,7 @@ nsScreenManagerProxy::InvalidateCacheOnNextTick() mCacheWillInvalidate = true; - nsCOMPtr r = - NS_NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache); - nsContentUtils::RunInStableState(r.forget()); + nsContentUtils::RunInStableState(NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache)); } void diff --git a/widget/qt/nsWindow.h b/widget/qt/nsWindow.h index 3943018b28..53a7b20ff5 100644 --- a/widget/qt/nsWindow.h +++ b/widget/qt/nsWindow.h @@ -312,7 +312,7 @@ private: void DispatchMotionToMainThread() { if (!mTimerStarted) { nsCOMPtr event = - NS_NewRunnableMethod(this, &nsWindow::ProcessMotionEvent); + mozilla::NewRunnableMethod(this, &nsWindow::ProcessMotionEvent); NS_DispatchToMainThread(event); mTimerStarted = true; } diff --git a/widget/windows/AudioSession.cpp b/widget/windows/AudioSession.cpp index 3d4b0f2f67..418a98b8ed 100644 --- a/widget/windows/AudioSession.cpp +++ b/widget/windows/AudioSession.cpp @@ -408,7 +408,7 @@ AudioSession::OnSessionDisconnected(AudioSessionDisconnectReason aReason) // Run our code asynchronously. Per MSDN we can't do anything interesting // in this callback. nsCOMPtr runnable = - NS_NewRunnableMethod(this, &AudioSession::OnSessionDisconnectedInternal); + NewRunnableMethod(this, &AudioSession::OnSessionDisconnectedInternal); NS_DispatchToMainThread(runnable); return S_OK; } diff --git a/widget/windows/LSPAnnotator.cpp b/widget/windows/LSPAnnotator.cpp index bbe55563e5..de4a40d2ac 100644 --- a/widget/windows/LSPAnnotator.cpp +++ b/widget/windows/LSPAnnotator.cpp @@ -145,7 +145,7 @@ LSPAnnotationGatherer::Run() } mString = str; - NS_DispatchToMainThread(NS_NewRunnableMethod(this, &LSPAnnotationGatherer::Annotate)); + NS_DispatchToMainThread(NewRunnableMethod(this, &LSPAnnotationGatherer::Annotate)); return NS_OK; } diff --git a/widget/windows/WinCompositorWidgetProxy.cpp b/widget/windows/WinCompositorWidgetProxy.cpp new file mode 100644 index 0000000000..3d4b48be20 --- /dev/null +++ b/widget/windows/WinCompositorWidgetProxy.cpp @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "WinCompositorWidgetProxy.h" +#include "nsWindow.h" +#include "VsyncDispatcher.h" +#include "mozilla/gfx/Point.h" + +namespace mozilla { +namespace widget { + +using namespace mozilla::gfx; + +WinCompositorWidgetProxy::WinCompositorWidgetProxy(nsWindow* aWindow) + : mWindow(aWindow), + mWidgetKey(reinterpret_cast(aWindow)), + mWnd(reinterpret_cast(aWindow->GetNativeData(NS_NATIVE_WINDOW))), + mTransparencyMode(aWindow->GetTransparencyMode()), + mMemoryDC(nullptr), + mCompositeDC(nullptr) +{ + MOZ_ASSERT(aWindow); + MOZ_ASSERT(!aWindow->Destroyed()); + MOZ_ASSERT(mWnd && ::IsWindow(mWnd)); +} + +void +WinCompositorWidgetProxy::OnDestroyWindow() +{ + mTransparentSurface = nullptr; + mMemoryDC = nullptr; +} + +bool +WinCompositorWidgetProxy::PreRender(layers::LayerManagerComposite* aManager) +{ + // This can block waiting for WM_SETTEXT to finish + // Using PreRender is unnecessarily pessimistic because + // we technically only need to block during the present call + // not all of compositor rendering + mPresentLock.Enter(); + return true; +} + +void +WinCompositorWidgetProxy::PostRender(layers::LayerManagerComposite* aManager) +{ + mPresentLock.Leave(); +} + +nsIWidget* +WinCompositorWidgetProxy::RealWidget() +{ + return mWindow; +} + +LayoutDeviceIntSize +WinCompositorWidgetProxy::GetClientSize() +{ + RECT r; + if (!::GetClientRect(mWnd, &r)) { + return LayoutDeviceIntSize(); + } + return LayoutDeviceIntSize( + r.right - r.left, + r.bottom - r.top); +} + +already_AddRefed +WinCompositorWidgetProxy::StartRemoteDrawing() +{ + MOZ_ASSERT(!mCompositeDC); + + RefPtr surf; + if (mTransparencyMode == eTransparencyTransparent) { + surf = EnsureTransparentSurface(); + } + + // Must call this after EnsureTransparentSurface(), since it could update + // the DC. + HDC dc = GetWindowSurface(); + if (!surf) { + if (!dc) { + return nullptr; + } + uint32_t flags = (mTransparencyMode == eTransparencyOpaque) ? 0 : + gfxWindowsSurface::FLAG_IS_TRANSPARENT; + surf = new gfxWindowsSurface(dc, flags); + } + + IntSize size = surf->GetSize(); + if (size.width <= 0 || size.height <= 0) { + if (dc) { + FreeWindowSurface(dc); + } + return nullptr; + } + + MOZ_ASSERT(!mCompositeDC); + mCompositeDC = dc; + + return mozilla::gfx::Factory::CreateDrawTargetForCairoSurface(surf->CairoSurface(), size); +} + +void +WinCompositorWidgetProxy::EndRemoteDrawing() +{ + if (mTransparencyMode == eTransparencyTransparent) { + MOZ_ASSERT(mTransparentSurface); + RedrawTransparentWindow(); + } + if (mCompositeDC) { + FreeWindowSurface(mCompositeDC); + } + mCompositeDC = nullptr; +} + +already_AddRefed +WinCompositorWidgetProxy::GetCompositorVsyncDispatcher() +{ + RefPtr cvd = mWindow->GetCompositorVsyncDispatcher(); + return cvd.forget(); +} + +uintptr_t +WinCompositorWidgetProxy::GetWidgetKey() +{ + return mWidgetKey; +} + +void +WinCompositorWidgetProxy::EnterPresentLock() +{ + mPresentLock.Enter(); +} + +void +WinCompositorWidgetProxy::LeavePresentLock() +{ + mPresentLock.Leave(); +} + +RefPtr +WinCompositorWidgetProxy::EnsureTransparentSurface() +{ + MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent); + + if (!mTransparentSurface) { + LayoutDeviceIntSize size = GetClientSize(); + CreateTransparentSurface(size.width, size.height); + } + + RefPtr surface = mTransparentSurface; + return surface.forget(); +} + +void +WinCompositorWidgetProxy::CreateTransparentSurface(int32_t aWidth, int32_t aHeight) +{ + MOZ_ASSERT(!mTransparentSurface && !mMemoryDC); + RefPtr surface = + new gfxWindowsSurface(IntSize(aWidth, aHeight), SurfaceFormat::A8R8G8B8_UINT32); + mTransparentSurface = surface; + mMemoryDC = surface->GetDC(); +} + +void +WinCompositorWidgetProxy::UpdateTransparency(nsTransparencyMode aMode) +{ + if (mTransparencyMode == aMode) { + return; + } + + mTransparencyMode = aMode; + mTransparentSurface = nullptr; + mMemoryDC = nullptr; + + if (mTransparencyMode == eTransparencyTransparent) { + EnsureTransparentSurface(); + } +} + +void +WinCompositorWidgetProxy::ClearTransparentWindow() +{ + if (!mTransparentSurface) { + return; + } + + IntSize size = mTransparentSurface->GetSize(); + RefPtr drawTarget = gfxPlatform::GetPlatform()-> + CreateDrawTargetForSurface(mTransparentSurface, size); + drawTarget->ClearRect(Rect(0, 0, size.width, size.height)); + RedrawTransparentWindow(); +} + +void +WinCompositorWidgetProxy::ResizeTransparentWindow(int32_t aNewWidth, int32_t aNewHeight) +{ + MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent); + + if (mTransparentSurface && + mTransparentSurface->GetSize() == IntSize(aNewWidth, aNewHeight)) + { + return; + } + + // Destroy the old surface. + mTransparentSurface = nullptr; + mMemoryDC = nullptr; + + CreateTransparentSurface(aNewWidth, aNewHeight); +} + +bool +WinCompositorWidgetProxy::RedrawTransparentWindow() +{ + MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent); + + LayoutDeviceIntSize size = GetClientSize(); + + ::GdiFlush(); + + BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + SIZE winSize = { size.width, size.height }; + POINT srcPos = { 0, 0 }; + HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true); + RECT winRect; + ::GetWindowRect(hWnd, &winRect); + + // perform the alpha blend + return !!::UpdateLayeredWindow( + hWnd, nullptr, (POINT*)&winRect, &winSize, mMemoryDC, + &srcPos, 0, &bf, ULW_ALPHA); +} + +HDC +WinCompositorWidgetProxy::GetWindowSurface() +{ + return eTransparencyTransparent == mTransparencyMode + ? mMemoryDC + : ::GetDC(mWnd); +} + +void +WinCompositorWidgetProxy::FreeWindowSurface(HDC dc) +{ + if (eTransparencyTransparent != mTransparencyMode) + ::ReleaseDC(mWnd, dc); +} + +} // namespace widget +} // namespace mozilla diff --git a/widget/windows/WinCompositorWidgetProxy.h b/widget/windows/WinCompositorWidgetProxy.h new file mode 100644 index 0000000000..bf65ca46c0 --- /dev/null +++ b/widget/windows/WinCompositorWidgetProxy.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 _widget_windows_WinCompositorWidget_h__ +#define _widget_windows_WinCompositorWidget_h__ + +#include "CompositorWidgetProxy.h" +#include "mozilla/gfx/CriticalSection.h" + +class nsWindow; + +namespace mozilla { +namespace widget { + +// This is the Windows-specific implementation of CompositorWidgetProxy. For +// the most part it only requires an HWND, however it maintains extra state +// for transparent windows, as well as for synchronizing WM_SETTEXT messages +// with the compositor. +class WinCompositorWidgetProxy : public CompositorWidgetProxy +{ +public: + WinCompositorWidgetProxy(nsWindow* aWindow); + + bool PreRender(layers::LayerManagerComposite*) override; + void PostRender(layers::LayerManagerComposite*) override; + already_AddRefed StartRemoteDrawing() override; + void EndRemoteDrawing() override; + LayoutDeviceIntSize GetClientSize() override; + already_AddRefed GetCompositorVsyncDispatcher() override; + uintptr_t GetWidgetKey() override; + nsIWidget* RealWidget() override; + WinCompositorWidgetProxy* AsWindowsProxy() override { + return this; + } + + // Callbacks for nsWindow. + void EnterPresentLock(); + void LeavePresentLock(); + void OnDestroyWindow(); + + // Transparency handling. + void UpdateTransparency(nsTransparencyMode aMode); + void ClearTransparentWindow(); + bool RedrawTransparentWindow(); + + // Update the bounds of the transparent surface. + void ResizeTransparentWindow(int32_t aNewWidth, int32_t aNewHeight); + + // Ensure that a transparent surface exists, then return it. + RefPtr EnsureTransparentSurface(); + + HDC GetTransparentDC() const { + return mMemoryDC; + } + HWND GetHwnd() const { + return mWnd; + } + +private: + HDC GetWindowSurface(); + void FreeWindowSurface(HDC dc); + + void CreateTransparentSurface(int32_t aWidth, int32_t aHeight); + +private: + nsWindow* mWindow; + uintptr_t mWidgetKey; + HWND mWnd; + gfx::CriticalSection mPresentLock; + + // Transparency handling. + nsTransparencyMode mTransparencyMode; + RefPtr mTransparentSurface; + HDC mMemoryDC; + HDC mCompositeDC; +}; + +} // namespace widget +} // namespace mozilla + +#endif // _widget_windows_WinCompositorWidget_h__ diff --git a/widget/windows/WinTextEventDispatcherListener.cpp b/widget/windows/WinTextEventDispatcherListener.cpp index 7ea20f59e2..156c7a79b1 100644 --- a/widget/windows/WinTextEventDispatcherListener.cpp +++ b/widget/windows/WinTextEventDispatcherListener.cpp @@ -8,6 +8,7 @@ #include "mozilla/widget/IMEData.h" #include "nsWindow.h" #include "WinIMEHandler.h" +#include "WinTextEventDispatcherListener.h" namespace mozilla { namespace widget { diff --git a/widget/windows/moz.build b/widget/windows/moz.build index 1da3a5b3d8..a6e4d90807 100644 --- a/widget/windows/moz.build +++ b/widget/windows/moz.build @@ -14,6 +14,7 @@ EXPORTS += [ EXPORTS.mozilla.widget += [ 'AudioSession.h', + 'WinCompositorWidgetProxy.h', 'WinMessages.h', 'WinModifierKeyState.h', 'WinNativeEventData.h', @@ -54,6 +55,7 @@ UNIFIED_SOURCES += [ 'TaskbarTabPreview.cpp', 'TaskbarWindowPreview.cpp', 'WidgetTraceEvent.cpp', + 'WinCompositorWidgetProxy.cpp', 'WindowHook.cpp', 'WinIMEHandler.cpp', 'WinTaskbar.cpp', diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 2c55fe8b65..1ed21fcbab 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -55,6 +55,7 @@ **************************************************************/ #include "gfx2DGlue.h" +#include "gfxEnv.h" #include "gfxPlatform.h" #include "gfxPrefs.h" #include "mozilla/MathAlgorithms.h" @@ -139,6 +140,7 @@ #include "nsBidiKeyboard.h" #include "nsThemeConstants.h" #include "gfxConfig.h" +#include "WinCompositorWidgetProxy.h" #include "nsIGfxInfo.h" #include "nsUXThemeConstants.h" @@ -394,7 +396,6 @@ nsWindow::nsWindow() mWnd = nullptr; mTransitionWnd = nullptr; mPaintDC = nullptr; - mCompositeDC = nullptr; mPrevWndProc = nullptr; mNativeDragTarget = nullptr; mInDtor = false; @@ -429,8 +430,6 @@ nsWindow::nsWindow() mCachedHitTestTime = TimeStamp::Now(); mCachedHitTestResult = 0; #ifdef MOZ_XUL - mTransparentSurface = nullptr; - mMemoryDC = nullptr; mTransparencyMode = eTransparencyOpaque; memset(&mGlassMargins, 0, sizeof mGlassMargins); #endif @@ -1301,7 +1300,9 @@ NS_METHOD nsWindow::Show(bool bState) // Clear contents to avoid ghosting of old content if we display // this window again. if (wasVisible && mTransparencyMode == eTransparencyTransparent) { - ClearTranslucentWindow(); + if (RefPtr proxy = GetCompositorWidgetProxy()) { + proxy->ClearTransparentWindow(); + } } if (mWindowType != eWindowType_dialog) { ::ShowWindow(mWnd, SW_HIDE); @@ -1557,10 +1558,11 @@ NS_METHOD nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) return NS_OK; } -#ifdef MOZ_XUL - if (eTransparencyTransparent == mTransparencyMode) - ResizeTranslucentWindow(width, height); -#endif + if (mTransparencyMode == eTransparencyTransparent) { + if (RefPtr proxy = GetCompositorWidgetProxy()) { + proxy->ResizeTransparentWindow(width, height); + } + } // Set cached value for lightweight and printing mBounds.width = width; @@ -1615,10 +1617,11 @@ NS_METHOD nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, return NS_OK; } -#ifdef MOZ_XUL - if (eTransparencyTransparent == mTransparencyMode) - ResizeTranslucentWindow(width, height); -#endif + if (eTransparencyTransparent == mTransparencyMode) { + if (RefPtr proxy = GetCompositorWidgetProxy()) { + proxy->ResizeTransparentWindow(width, height); + } + } // Set cached value for lightweight and printing mBounds.x = x; @@ -3208,27 +3211,6 @@ nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) return rv; } -HDC nsWindow::GetWindowSurface() -{ -#ifdef MOZ_XUL - return eTransparencyTransparent == mTransparencyMode - ? mMemoryDC - : ::GetDC(mWnd); -#else - return ::GetDC(mWnd); -#endif -} - -void nsWindow::FreeWindowSurface(HDC dc) -{ -#ifdef MOZ_XUL - if (eTransparencyTransparent != mTransparencyMode) - ::ReleaseDC(mWnd, dc); -#else - ::ReleaseDC(mWnd, dc); -#endif -} - /************************************************************** * * SECTION: Native data storage @@ -3649,7 +3631,14 @@ nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, } if (!mLayerManager) { - MOZ_ASSERT(!mCompositorBridgeParent && !mCompositorBridgeChild); + MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild); + + // Ensure we have a widget proxy even if we're not using the compositor, + // since that's where we handle transparent windows. + if (!mCompositorWidgetProxy) { + mCompositorWidgetProxy = new WinCompositorWidgetProxy(this); + } + mLayerManager = CreateBasicLayerManager(); } @@ -3710,57 +3699,16 @@ nsWindow::OnDefaultButtonLoaded(const LayoutDeviceIntRect& aButtonRect) return NS_OK; } -already_AddRefed -nsWindow::StartRemoteDrawing() +mozilla::widget::CompositorWidgetProxy* +nsWindow::NewCompositorWidgetProxy() { - MOZ_ASSERT(!mCompositeDC); - HDC dc = GetWindowSurface(); - RefPtr surf; - - if (mTransparencyMode == eTransparencyTransparent) { - if (!mTransparentSurface) { - SetupTranslucentWindowMemoryBitmap(mTransparencyMode); - } - if (mTransparentSurface) { - surf = mTransparentSurface; - } - } - - if (!surf) { - if (!dc) { - return nullptr; - } - uint32_t flags = (mTransparencyMode == eTransparencyOpaque) ? 0 : - gfxWindowsSurface::FLAG_IS_TRANSPARENT; - surf = new gfxWindowsSurface(dc, flags); - } - - mozilla::gfx::IntSize size(surf->GetSize().width, surf->GetSize().height); - if (size.width <= 0 || size.height <= 0) { - if (dc) { - FreeWindowSurface(dc); - } - return nullptr; - } - - MOZ_ASSERT(!mCompositeDC); - mCompositeDC = dc; - - return mozilla::gfx::Factory::CreateDrawTargetForCairoSurface(surf->CairoSurface(), size); + return new WinCompositorWidgetProxy(this); } -void -nsWindow::EndRemoteDrawing() +mozilla::widget::WinCompositorWidgetProxy* +nsWindow::GetCompositorWidgetProxy() { - if (mTransparencyMode == eTransparencyTransparent) { - MOZ_ASSERT(gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() - || mTransparentSurface); - UpdateTranslucentWindow(); - } - if (mCompositeDC) { - FreeWindowSurface(mCompositeDC); - } - mCompositeDC = nullptr; + return mCompositorWidgetProxy ? mCompositorWidgetProxy->AsWindowsProxy() : nullptr; } void @@ -4971,13 +4919,18 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, // // To do this we take mPresentLock in nsWindow::PreRender and // if that lock is taken we wait before doing WM_SETTEXT - mPresentLock.Enter(); + RefPtr proxy = GetCompositorWidgetProxy(); + if (proxy) { + proxy->EnterPresentLock(); + } DWORD style = GetWindowLong(mWnd, GWL_STYLE); SetWindowLong(mWnd, GWL_STYLE, style & ~WS_VISIBLE); *aRetValue = CallWindowProcW(GetPrevWindowProc(), mWnd, msg, wParam, lParam); SetWindowLong(mWnd, GWL_STYLE, style); - mPresentLock.Leave(); + if (proxy) { + proxy->LeavePresentLock(); + } return true; } @@ -4993,6 +4946,13 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, if (!mCustomNonClient) break; + // There is a case that rendered result is not kept. Bug 1237617 + if (wParam == TRUE && + !gfxEnv::DisableForcePresent() && + gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) { + NS_DispatchToMainThread(NewRunnableMethod(this, &nsWindow::ForcePresent)); + } + // let the dwm handle nc painting on glass // Never allow native painting if we are on fullscreen if(mSizeMode != nsSizeMode_Fullscreen && @@ -6269,10 +6229,11 @@ void nsWindow::OnWindowPosChanged(WINDOWPOS* wp) newHeight = r.bottom - r.top; nsIntRect rect(wp->x, wp->y, newWidth, newHeight); -#ifdef MOZ_XUL - if (eTransparencyTransparent == mTransparencyMode) - ResizeTranslucentWindow(newWidth, newHeight); -#endif + if (eTransparencyTransparent == mTransparencyMode) { + if (RefPtr proxy = GetCompositorWidgetProxy()) { + proxy->ResizeTransparentWindow(newWidth, newHeight); + } + } if (newWidth > mLastSize.width) { @@ -6790,11 +6751,9 @@ void nsWindow::OnDestroy() if (mCursor == -1) SetCursor(eCursor_standard); -#ifdef MOZ_XUL - // Reset transparency - if (eTransparencyTransparent == mTransparencyMode) - SetupTranslucentWindowMemoryBitmap(eTransparencyOpaque); -#endif + if (RefPtr proxy = GetCompositorWidgetProxy()) { + proxy->OnDestroyWindow(); + } // Finalize panning feedback to possibly restore window displacement mGesture.PanFeedbackFinalize(mWnd, true); @@ -7078,17 +7037,6 @@ nsWindow::GetAccessible() #ifdef MOZ_XUL -void nsWindow::ResizeTranslucentWindow(int32_t aNewWidth, int32_t aNewHeight, bool force) -{ - if (!force && aNewWidth == mBounds.width && aNewHeight == mBounds.height) - return; - - RefPtr newSurface = - new gfxWindowsSurface(IntSize(aNewWidth, aNewHeight), SurfaceFormat::A8R8G8B8_UINT32); - mTransparentSurface = newSurface; - mMemoryDC = newSurface->GetDC(); -} - void nsWindow::SetWindowTranslucencyInner(nsTransparencyMode aMode) { if (aMode == mTransparencyMode) @@ -7140,57 +7088,12 @@ void nsWindow::SetWindowTranslucencyInner(nsTransparencyMode aMode) memset(&mGlassMargins, 0, sizeof mGlassMargins); mTransparencyMode = aMode; - SetupTranslucentWindowMemoryBitmap(aMode); + if (RefPtr proxy = GetCompositorWidgetProxy()) { + proxy->UpdateTransparency(aMode); + } UpdateGlass(); } -void nsWindow::SetupTranslucentWindowMemoryBitmap(nsTransparencyMode aMode) -{ - if (eTransparencyTransparent == aMode) { - ResizeTranslucentWindow(mBounds.width, mBounds.height, true); - } else { - mTransparentSurface = nullptr; - mMemoryDC = nullptr; - } -} - -void nsWindow::ClearTranslucentWindow() -{ - if (mTransparentSurface) { - IntSize size = mTransparentSurface->GetSize(); - RefPtr drawTarget = gfxPlatform::GetPlatform()-> - CreateDrawTargetForSurface(mTransparentSurface, size); - drawTarget->ClearRect(Rect(0, 0, size.width, size.height)); - UpdateTranslucentWindow(); - } -} - -nsresult nsWindow::UpdateTranslucentWindow() -{ - if (mBounds.IsEmpty()) - return NS_OK; - - ::GdiFlush(); - - BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - SIZE winSize = { mBounds.width, mBounds.height }; - POINT srcPos = { 0, 0 }; - HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true); - RECT winRect; - ::GetWindowRect(hWnd, &winRect); - - // perform the alpha blend - bool updateSuccesful = - ::UpdateLayeredWindow(hWnd, nullptr, (POINT*)&winRect, &winSize, mMemoryDC, - &srcPos, 0, &bf, ULW_ALPHA); - - if (!updateSuccesful) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - #endif //MOZ_XUL /************************************************************** @@ -7829,21 +7732,6 @@ void nsWindow::PickerClosed() } } -bool nsWindow::PreRender(LayerManagerComposite*) -{ - // This can block waiting for WM_SETTEXT to finish - // Using PreRender is unnecessarily pessimistic because - // we technically only need to block during the present call - // not all of compositor rendering - mPresentLock.Enter(); - return true; -} - -void nsWindow::PostRender(LayerManagerComposite*) -{ - mPresentLock.Leave(); -} - bool nsWindow::ComputeShouldAccelerate() { diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index ecad587fa4..386ba048b8 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -25,7 +25,6 @@ #include "nsITimer.h" #include "nsRegion.h" #include "mozilla/EventForwards.h" -#include "mozilla/gfx/CriticalSection.h" #include "mozilla/MouseEvents.h" #include "mozilla/TimeStamp.h" #include "nsMargin.h" @@ -59,6 +58,7 @@ namespace mozilla { namespace widget { class NativeKey; struct MSGResult; +class WinCompositorWidgetProxy; } // namespace widget } // namespacw mozilla; @@ -211,8 +211,6 @@ public: NS_IMETHOD GetNonClientMargins(LayoutDeviceIntMargin& aMargins) override; NS_IMETHOD SetNonClientMargins(LayoutDeviceIntMargin& aMargins) override; void SetDrawsInTitlebar(bool aState) override; - already_AddRefed StartRemoteDrawing() override; - virtual void EndRemoteDrawing() override; virtual void UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion) override; virtual void UpdateThemeGeometries(const nsTArray& aThemeGeometries) override; @@ -315,6 +313,8 @@ public: const mozilla::NativeEventData& aKeyEventData, nsIKeyEventInPluginCallback* aCallback) override; + mozilla::widget::CompositorWidgetProxy* NewCompositorWidgetProxy() override; + protected: virtual ~nsWindow(); @@ -468,17 +468,10 @@ protected: private: void SetWindowTranslucencyInner(nsTransparencyMode aMode); nsTransparencyMode GetWindowTranslucencyInner() const { return mTransparencyMode; } - void ResizeTranslucentWindow(int32_t aNewWidth, int32_t aNewHeight, bool force = false); - nsresult UpdateTranslucentWindow(); - void ClearTranslucentWindow(); - void SetupTranslucentWindowMemoryBitmap(nsTransparencyMode aMode); void UpdateGlass(); protected: #endif // MOZ_XUL - HDC GetWindowSurface(); - void FreeWindowSurface(HDC dc); - static bool IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult); void IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam); @@ -495,8 +488,7 @@ protected: void ClearCachedResources(); nsIWidgetListener* GetPaintListener(); - virtual bool PreRender(LayerManagerComposite*) override; - virtual void PostRender(LayerManagerComposite*) override; + mozilla::widget::WinCompositorWidgetProxy* GetCompositorWidgetProxy(); protected: nsCOMPtr mParent; @@ -593,7 +585,6 @@ protected: // Graphics HDC mPaintDC; // only set during painting - HDC mCompositeDC; // only set during StartRemoteDrawing LayoutDeviceIntRect mLastPaintBounds; @@ -601,9 +592,6 @@ protected: // Transparency #ifdef MOZ_XUL - // Use layered windows to support full 256 level alpha translucency - RefPtr mTransparentSurface; - HDC mMemoryDC; nsTransparencyMode mTransparencyMode; nsIntRegion mPossiblyTransparentRegion; MARGINS mGlassMargins; @@ -637,8 +625,6 @@ protected: static bool sNeedsToInitMouseWheelSettings; static void InitMouseWheelScrollData(); - mozilla::gfx::CriticalSection mPresentLock; - double mSizeConstraintsScale; // scale in effect when setting constraints }; diff --git a/widget/windows/nsWindowGfx.cpp b/widget/windows/nsWindowGfx.cpp index 264d42d36d..0dcf6bf8be 100644 --- a/widget/windows/nsWindowGfx.cpp +++ b/widget/windows/nsWindowGfx.cpp @@ -215,12 +215,10 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) ClientLayerManager *clientLayerManager = GetLayerManager()->AsClientLayerManager(); - if (clientLayerManager && mCompositorBridgeParent && - !mBounds.IsEqualEdges(mLastPaintBounds)) - { + if (clientLayerManager && !mBounds.IsEqualEdges(mLastPaintBounds)) { // Do an early async composite so that we at least have something on the // screen in the right place, even if the content is out of date. - mCompositorBridgeParent->ScheduleRenderOnCompositorThread(); + clientLayerManager->Composite(); } mLastPaintBounds = mBounds; @@ -238,7 +236,8 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) ::BeginPaint(mWnd, &ps); ::EndPaint(mWnd, &ps); - aDC = mMemoryDC; + // We're guaranteed to have a widget proxy since we called GetLayerManager(). + aDC = GetCompositorWidgetProxy()->GetTransparentDC(); } #endif @@ -266,7 +265,7 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) #endif nsIntRegion region = GetRegionToPaint(forceRepaint, ps, hDC); - if (clientLayerManager && mCompositorBridgeParent) { + if (clientLayerManager) { // We need to paint to the screen even if nothing changed, since if we // don't have a compositing window manager, our pixels could be stale. clientLayerManager->SetNeedsComposite(true); @@ -283,8 +282,8 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) return false; } - if (clientLayerManager && mCompositorBridgeParent && clientLayerManager->NeedsComposite()) { - mCompositorBridgeParent->ScheduleRenderOnCompositorThread(); + if (clientLayerManager && clientLayerManager->NeedsComposite()) { + clientLayerManager->Composite(); clientLayerManager->SetNeedsComposite(false); } @@ -310,10 +309,7 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) #if defined(MOZ_XUL) // don't support transparency for non-GDI rendering, for now if (eTransparencyTransparent == mTransparencyMode) { - if (mTransparentSurface == nullptr) { - SetupTranslucentWindowMemoryBitmap(mTransparencyMode); - } - targetSurface = mTransparentSurface; + targetSurface = GetCompositorWidgetProxy()->EnsureTransparentSurface(); } #endif @@ -378,7 +374,7 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) // Data from offscreen drawing surface was copied to memory bitmap of transparent // bitmap. Now it can be read from memory bitmap to apply alpha channel and after // that displayed on the screen. - UpdateTranslucentWindow(); + GetCompositorWidgetProxy()->RedrawTransparentWindow(); } #endif } @@ -389,7 +385,7 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) this, LayoutDeviceIntRegion::FromUnknownRegion(region)); if (!gfxEnv::DisableForcePresent() && gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) { nsCOMPtr event = - NS_NewRunnableMethod(this, &nsWindow::ForcePresent); + NewRunnableMethod(this, &nsWindow::ForcePresent); NS_DispatchToMainThread(event); } } diff --git a/xpcom/base/nsDumpUtils.cpp b/xpcom/base/nsDumpUtils.cpp index b9d9eb4f5c..33dceb47c6 100644 --- a/xpcom/base/nsDumpUtils.cpp +++ b/xpcom/base/nsDumpUtils.cpp @@ -74,8 +74,7 @@ FdWatcher::Init() nsCOMPtr os = services::GetObserverService(); os->AddObserver(this, "xpcom-shutdown", /* ownsWeak = */ false); - XRE_GetIOMessageLoop()->PostTask( - NewRunnableMethod(this, &FdWatcher::StartWatching)); + XRE_GetIOMessageLoop()->PostTask(NewRunnableMethod(this, &FdWatcher::StartWatching)); } // Implementations may call this function multiple times if they ensure that diff --git a/xpcom/base/nsDumpUtils.h b/xpcom/base/nsDumpUtils.h index 46951f59f8..12a99da18d 100644 --- a/xpcom/base/nsDumpUtils.h +++ b/xpcom/base/nsDumpUtils.h @@ -89,8 +89,7 @@ public: MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown")); - XRE_GetIOMessageLoop()->PostTask( - NewRunnableMethod(this, &FdWatcher::StopWatching)); + XRE_GetIOMessageLoop()->PostTask(mozilla::NewRunnableMethod(this, &FdWatcher::StopWatching)); return NS_OK; } diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 99323550b3..9fe27424e7 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -1695,8 +1695,9 @@ nsMemoryReporterManager::GetReportsExtended( aDMDDumpIdent); if (aMinimize) { - rv = MinimizeMemoryUsage(NS_NewRunnableMethod( - this, &nsMemoryReporterManager::StartGettingReports)); + nsCOMPtr callback = + NewRunnableMethod(this, &nsMemoryReporterManager::StartGettingReports); + rv = MinimizeMemoryUsage(callback); } else { rv = StartGettingReports(); } diff --git a/xpcom/ds/nsObserverService.cpp b/xpcom/ds/nsObserverService.cpp index 42bcff58be..455764811e 100644 --- a/xpcom/ds/nsObserverService.cpp +++ b/xpcom/ds/nsObserverService.cpp @@ -200,9 +200,7 @@ nsObserverService::Create(nsISupports* aOuter, const nsIID& aIID, // The memory reporter can not be immediately registered here because // the nsMemoryReporterManager may attempt to get the nsObserverService // during initialization, causing a recursive GetService. - RefPtr> registerRunnable = - NS_NewRunnableMethod(os, &nsObserverService::RegisterReporter); - NS_DispatchToCurrentThread(registerRunnable); + NS_DispatchToCurrentThread(NewRunnableMethod(os, &nsObserverService::RegisterReporter)); return os->QueryInterface(aIID, aInstancePtr); } diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h index b9bb171ab1..0cf96b6219 100644 --- a/xpcom/glue/nsThreadUtils.h +++ b/xpcom/glue/nsThreadUtils.h @@ -283,8 +283,11 @@ nsRunnableFunction* NS_NewRunnableFunction(const Function& aFunction) // with nsRevocableEventPtr. template -class nsRunnableMethod : public mozilla::Runnable + bool Owning = true, + bool Cancelable = false> +class nsRunnableMethod : public mozilla::Conditional::Type { public: virtual void Revoke() = 0; @@ -328,31 +331,60 @@ struct nsRunnableMethodReceiver void Revoke() { mObj = nullptr; } }; -template struct nsRunnableMethodTraits; +template struct nsRunnableMethodTraits; -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Cancelable; +}; + +template +struct nsRunnableMethodTraits +{ + typedef const C class_type; + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Cancelable; }; #ifdef NS_HAVE_STDCALL -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Cancelable; }; -template -struct nsRunnableMethodTraits +template +struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; - typedef nsRunnableMethod base_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Cancelable; +}; +template +struct nsRunnableMethodTraits +{ + typedef const C class_type; + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Cancelable; +}; + +template +struct nsRunnableMethodTraits +{ + typedef const C class_type; + typedef R return_type; + typedef nsRunnableMethod base_type; + static const bool can_cancel = Cancelable; }; #endif @@ -683,11 +715,11 @@ struct nsRunnableMethodArguments } }; -template +template class nsRunnableMethodImpl - : public nsRunnableMethodTraits::base_type + : public nsRunnableMethodTraits::base_type { - typedef typename nsRunnableMethodTraits::class_type + typedef typename nsRunnableMethodTraits::class_type ClassType; nsRunnableMethodReceiver mReceiver; Method mMethod; @@ -710,70 +742,102 @@ public: } return NS_OK; } + nsresult Cancel() { + static_assert(Cancelable, "Don't use me!"); + Revoke(); + return NS_OK; + } void Revoke() { mReceiver.Revoke(); } }; // Use this template function like so: // // nsCOMPtr event = -// NS_NewRunnableMethod(myObject, &MyClass::HandleEvent); +// mozilla::NewRunnableMethod(myObject, &MyClass::HandleEvent); // NS_DispatchToCurrentThread(event); // // Statically enforced constraints: // - myObject must be of (or implicitly convertible to) type MyClass // - MyClass must defined AddRef and Release methods // + +namespace mozilla { + template -typename nsRunnableMethodTraits::base_type* -NS_NewRunnableMethod(PtrType aPtr, Method aMethod) +already_AddRefed::base_type> +NewRunnableMethod(PtrType aPtr, Method aMethod) { - return new nsRunnableMethodImpl(aPtr, aMethod); + return do_AddRef(new nsRunnableMethodImpl(aPtr, aMethod)); } template -typename nsRunnableMethodTraits::base_type* -NS_NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod) +already_AddRefed::base_type> +NewCancelableRunnableMethod(PtrType aPtr, Method aMethod) { - return new nsRunnableMethodImpl(aPtr, aMethod); + return do_AddRef(new nsRunnableMethodImpl(aPtr, aMethod)); } -// Similar to NS_NewRunnableMethod. Call like so: -// nsCOMPtr event = -// NS_NewRunnableMethodWithArg(myObject, &MyClass::HandleEvent, myArg); -// 'Type' is the stored type for the argument, see ParameterStorage for details. -template -typename nsRunnableMethodTraits::base_type* -NS_NewRunnableMethodWithArg(PtrType&& aPtr, Method aMethod, Arg&& aArg) +template +already_AddRefed::base_type> +NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod) { - return new nsRunnableMethodImpl( - aPtr, aMethod, mozilla::Forward(aArg)); + return do_AddRef(new nsRunnableMethodImpl(aPtr, aMethod)); } -// Similar to NS_NewRunnableMethod. Call like so: +template +already_AddRefed::base_type> +NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod) +{ + return do_AddRef(new nsRunnableMethodImpl(aPtr, aMethod)); +} + +// Similar to NewRunnableMethod. Call like so: // nsCOMPtr event = -// NS_NewRunnableMethodWithArg(myObject, &MyClass::HandleEvent, myArg1,...); +// NewRunnableMethod(myObject, &MyClass::HandleEvent, myArg1,...); // 'Types' are the stored type for each argument, see ParameterStorage for details. template -typename nsRunnableMethodTraits::base_type* -NS_NewRunnableMethodWithArgs(PtrType&& aPtr, Method aMethod, Args&&... aArgs) +already_AddRefed::base_type> +NewRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs) { static_assert(sizeof...(Storages) == sizeof...(Args), " size should be equal to number of arguments"); - return new nsRunnableMethodImpl( - aPtr, aMethod, mozilla::Forward(aArgs)...); + return do_AddRef(new nsRunnableMethodImpl( + aPtr, aMethod, mozilla::Forward(aArgs)...)); } template -typename nsRunnableMethodTraits::base_type* -NS_NewNonOwningRunnableMethodWithArgs(PtrType&& aPtr, Method aMethod, - Args&&... aArgs) +already_AddRefed::base_type> +NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs) { static_assert(sizeof...(Storages) == sizeof...(Args), " size should be equal to number of arguments"); - return new nsRunnableMethodImpl( - aPtr, aMethod, mozilla::Forward(aArgs)...); + return do_AddRef(new nsRunnableMethodImpl( + aPtr, aMethod, mozilla::Forward(aArgs)...)); } +template +already_AddRefed::base_type> +NewCancelableRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs) +{ + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return do_AddRef(new nsRunnableMethodImpl( + aPtr, aMethod, mozilla::Forward(aArgs)...)); +} + +template +already_AddRefed::base_type> +NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod, + Args&&... aArgs) +{ + static_assert(sizeof...(Storages) == sizeof...(Args), + " size should be equal to number of arguments"); + return do_AddRef(new nsRunnableMethodImpl( + aPtr, aMethod, mozilla::Forward(aArgs)...)); +} + +} // namespace mozilla + #endif // XPCOM_GLUE_AVOID_NSPR // This class is designed to be used when you have an event class E that has a @@ -837,6 +901,16 @@ public: return *this; } + const nsRevocableEventPtr& operator=(already_AddRefed aEvent) + { + RefPtr event = aEvent; + if (mEvent != event) { + Revoke(); + mEvent = event.forget(); + } + return *this; + } + void Revoke() { if (mEvent) { diff --git a/xpcom/glue/tests/gtest/TestThreadUtils.cpp b/xpcom/glue/tests/gtest/TestThreadUtils.cpp index ac4b9ef0d8..728bae612a 100644 --- a/xpcom/glue/tests/gtest/TestThreadUtils.cpp +++ b/xpcom/glue/tests/gtest/TestThreadUtils.cpp @@ -290,18 +290,18 @@ TEST(ThreadUtils, main) // Test legacy functions. nsCOMPtr r1 = - NS_NewRunnableMethod(rpt, &ThreadUtilsObject::Test0); + NewRunnableMethod(rpt, &ThreadUtilsObject::Test0); r1->Run(); EXPECT_EQ(count += 1, rpt->mCount); - r1 = NS_NewRunnableMethodWithArg(rpt, &ThreadUtilsObject::Test1i, 11); + r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1i, 11); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(11, rpt->mA0); // Test variadic function with simple POD arguments. - r1 = NS_NewRunnableMethodWithArgs(rpt, &ThreadUtilsObject::Test0); + r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test0); r1->Run(); EXPECT_EQ(count += 1, rpt->mCount); @@ -314,19 +314,19 @@ TEST(ThreadUtils, main) StoreCopyPassByValue>::value, "detail::ParameterStorage>::Type should be StoreCopyPassByValue"); - r1 = NS_NewRunnableMethodWithArgs(rpt, &ThreadUtilsObject::Test1i, 12); + r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1i, 12); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(12, rpt->mA0); - r1 = NS_NewRunnableMethodWithArgs( + r1 = NewRunnableMethod( rpt, &ThreadUtilsObject::Test2i, 21, 22); r1->Run(); EXPECT_EQ(count += 3, rpt->mCount); EXPECT_EQ(21, rpt->mA0); EXPECT_EQ(22, rpt->mA1); - r1 = NS_NewRunnableMethodWithArgs( + r1 = NewRunnableMethod( rpt, &ThreadUtilsObject::Test3i, 31, 32, 33); r1->Run(); EXPECT_EQ(count += 4, rpt->mCount); @@ -334,7 +334,7 @@ TEST(ThreadUtils, main) EXPECT_EQ(32, rpt->mA1); EXPECT_EQ(33, rpt->mA2); - r1 = NS_NewRunnableMethodWithArgs( + r1 = NewRunnableMethod( rpt, &ThreadUtilsObject::Test4i, 41, 42, 43, 44); r1->Run(); EXPECT_EQ(count += 5, rpt->mCount); @@ -347,7 +347,7 @@ TEST(ThreadUtils, main) // Passing a short to make sure forwarding works with an inexact type match. short int si = 11; - r1 = NS_NewRunnableMethodWithArgs(rpt, &ThreadUtilsObject::Test1i, si); + r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1i, si); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(si, rpt->mA0); @@ -373,7 +373,7 @@ TEST(ThreadUtils, main) "detail::ParameterStorage::Type::passed_type should be int*"); { int i = 12; - r1 = NS_NewRunnableMethodWithArgs(rpt, &ThreadUtilsObject::Test1pi, &i); + r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1pi, &i); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(i, rpt->mA0); @@ -400,7 +400,7 @@ TEST(ThreadUtils, main) "detail::ParameterStorage::Type::passed_type should be const int*"); { int i = 1201; - r1 = NS_NewRunnableMethodWithArgs(rpt, &ThreadUtilsObject::Test1pci, &i); + r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1pci, &i); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(i, rpt->mA0); @@ -415,7 +415,7 @@ TEST(ThreadUtils, main) "StoreCopyPassByPtr::passed_type should be int*"); { int i = 1202; - r1 = NS_NewRunnableMethodWithArgs>( + r1 = NewRunnableMethod>( rpt, &ThreadUtilsObject::Test1pi, i); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); @@ -431,7 +431,7 @@ TEST(ThreadUtils, main) "StoreCopyPassByConstPtr::passed_type should be const int*"); { int i = 1203; - r1 = NS_NewRunnableMethodWithArgs>( + r1 = NewRunnableMethod>( rpt, &ThreadUtilsObject::Test1pci, i); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); @@ -491,7 +491,7 @@ TEST(ThreadUtils, main) "ParameterStorage::Type::passed_type should be int&"); { int i = 13; - r1 = NS_NewRunnableMethodWithArgs(rpt, &ThreadUtilsObject::Test1ri, i); + r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test1ri, i); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); EXPECT_EQ(i, rpt->mA0); @@ -512,7 +512,7 @@ TEST(ThreadUtils, main) "ParameterStorage::Type::passed_type should be int&&"); { int i = 14; - r1 = NS_NewRunnableMethodWithArgs( + r1 = NewRunnableMethod( rpt, &ThreadUtilsObject::Test1rri, mozilla::Move(i)); } r1->Run(); @@ -534,7 +534,7 @@ TEST(ThreadUtils, main) "ParameterStorage&&>::Type::passed_type should be UniquePtr&&"); { mozilla::UniquePtr upi; - r1 = NS_NewRunnableMethodWithArgs&&>( + r1 = NewRunnableMethod&&>( rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); } r1->Run(); @@ -557,7 +557,7 @@ TEST(ThreadUtils, main) "ParameterStorage>>::Type::passed_type should be UniquePtr&&"); { mozilla::UniquePtr upi; - r1 = NS_NewRunnableMethodWithArgs + r1 = NewRunnableMethod >>( rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); } @@ -568,7 +568,7 @@ TEST(ThreadUtils, main) // Unique pointer as xvalue. { mozilla::UniquePtr upi = mozilla::MakeUnique(1); - r1 = NS_NewRunnableMethodWithArgs&&>( + r1 = NewRunnableMethod&&>( rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); } r1->Run(); @@ -577,7 +577,7 @@ TEST(ThreadUtils, main) { mozilla::UniquePtr upi = mozilla::MakeUnique(1); - r1 = NS_NewRunnableMethodWithArgs + r1 = NewRunnableMethod >> (rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi)); } @@ -586,7 +586,7 @@ TEST(ThreadUtils, main) EXPECT_EQ(1, rpt->mA0); // Unique pointer as prvalue. - r1 = NS_NewRunnableMethodWithArgs&&>( + r1 = NewRunnableMethod&&>( rpt, &ThreadUtilsObject::Test1upi, mozilla::MakeUnique(2)); r1->Run(); EXPECT_EQ(count += 2, rpt->mCount); @@ -595,7 +595,7 @@ TEST(ThreadUtils, main) // Unique pointer as lvalue to lref. { mozilla::UniquePtr upi; - r1 = NS_NewRunnableMethodWithArgs&>( + r1 = NewRunnableMethod&>( rpt, &ThreadUtilsObject::Test1rupi, upi); // Passed as lref, so Run() must be called while local upi is still alive! r1->Run(); @@ -614,8 +614,8 @@ TEST(ThreadUtils, main) Spy s(10); EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); - if (gDebug) { printf("%d - r2 = NS_NewRunnableMethodWithArgs>(&TestByValue, s)\n", __LINE__); } - r2 = NS_NewRunnableMethodWithArgs>( + if (gDebug) { printf("%d - r2 = NewRunnableMethod>(&TestByValue, s)\n", __LINE__); } + r2 = NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByValue, s); EXPECT_EQ(2, gAlive); EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction. @@ -641,9 +641,9 @@ TEST(ThreadUtils, main) Spy::ClearAll(); if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by value\n", __LINE__); } { - if (gDebug) { printf("%d - r3 = NS_NewRunnableMethodWithArgs>(&TestByValue, Spy(11))\n", __LINE__); } + if (gDebug) { printf("%d - r3 = NewRunnableMethod>(&TestByValue, Spy(11))\n", __LINE__); } nsCOMPtr r3 = - NS_NewRunnableMethodWithArgs>( + NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByValue, Spy(11)); EXPECT_EQ(1, gAlive); EXPECT_EQ(1, gConstructions); @@ -670,7 +670,7 @@ TEST(ThreadUtils, main) EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); Spy::ClearActions(); - r4 = NS_NewRunnableMethodWithArgs>( + r4 = NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByValue, mozilla::Move(s)); EXPECT_LE(1, gMoveConstructions); EXPECT_EQ(1, gAlive); @@ -701,8 +701,8 @@ TEST(ThreadUtils, main) Spy s(20); EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); - if (gDebug) { printf("%d - r5 = NS_NewRunnableMethodWithArgs>(&TestByConstLRef, s)\n", __LINE__); } - r5 = NS_NewRunnableMethodWithArgs>( + if (gDebug) { printf("%d - r5 = NewRunnableMethod>(&TestByConstLRef, s)\n", __LINE__); } + r5 = NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByConstLRef, s); EXPECT_EQ(2, gAlive); EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction. @@ -728,9 +728,9 @@ TEST(ThreadUtils, main) Spy::ClearAll(); if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by const lvalue ref\n", __LINE__); } { - if (gDebug) { printf("%d - r6 = NS_NewRunnableMethodWithArgs>(&TestByConstLRef, Spy(21))\n", __LINE__); } + if (gDebug) { printf("%d - r6 = NewRunnableMethod>(&TestByConstLRef, Spy(21))\n", __LINE__); } nsCOMPtr r6 = - NS_NewRunnableMethodWithArgs>( + NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByConstLRef, Spy(21)); EXPECT_EQ(1, gAlive); EXPECT_EQ(1, gConstructions); @@ -758,8 +758,8 @@ TEST(ThreadUtils, main) Spy s(30); EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); - if (gDebug) { printf("%d - r7 = NS_NewRunnableMethodWithArgs>(&TestByRRef, s)\n", __LINE__); } - r7 = NS_NewRunnableMethodWithArgs>( + if (gDebug) { printf("%d - r7 = NewRunnableMethod>(&TestByRRef, s)\n", __LINE__); } + r7 = NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByRRef, s); EXPECT_EQ(2, gAlive); EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction. @@ -786,9 +786,9 @@ TEST(ThreadUtils, main) Spy::ClearAll(); if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by rvalue ref\n", __LINE__); } { - if (gDebug) { printf("%d - r8 = NS_NewRunnableMethodWithArgs>(&TestByRRef, Spy(31))\n", __LINE__); } + if (gDebug) { printf("%d - r8 = NewRunnableMethod>(&TestByRRef, Spy(31))\n", __LINE__); } nsCOMPtr r8 = - NS_NewRunnableMethodWithArgs>( + NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByRRef, Spy(31)); EXPECT_EQ(1, gAlive); EXPECT_EQ(1, gConstructions); @@ -816,9 +816,9 @@ TEST(ThreadUtils, main) EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); Spy::ClearActions(); - if (gDebug) { printf("%d - r9 = NS_NewRunnableMethodWithArgs(&TestByLRef, s)\n", __LINE__); } + if (gDebug) { printf("%d - r9 = NewRunnableMethod(&TestByLRef, s)\n", __LINE__); } nsCOMPtr r9 = - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( rpt, &ThreadUtilsObject::TestByLRef, s); EXPECT_EQ(0, gAllConstructions); EXPECT_EQ(0, gDestructions); @@ -849,8 +849,8 @@ TEST(ThreadUtils, main) ptr = s.get(); EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); - if (gDebug) { printf("%d - r10 = NS_NewRunnableMethodWithArgs>(&TestByRRef, s.get())\n", __LINE__); } - r10 = NS_NewRunnableMethodWithArgs>( + if (gDebug) { printf("%d - r10 = NewRunnableMethod>(&TestByRRef, s.get())\n", __LINE__); } + r10 = NewRunnableMethod>( rpt, &ThreadUtilsObject::TestByPointer, s.get()); EXPECT_LE(0, gAllConstructions); EXPECT_EQ(1, gAlive); @@ -882,9 +882,9 @@ TEST(ThreadUtils, main) EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); Spy::ClearActions(); - if (gDebug) { printf("%d - r11 = NS_NewRunnableMethodWithArgs(&TestByPointer, s)\n", __LINE__); } + if (gDebug) { printf("%d - r11 = NewRunnableMethod(&TestByPointer, s)\n", __LINE__); } nsCOMPtr r11 = - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( rpt, &ThreadUtilsObject::TestByPointer, &s); EXPECT_EQ(0, gAllConstructions); EXPECT_EQ(0, gDestructions); @@ -912,9 +912,9 @@ TEST(ThreadUtils, main) EXPECT_EQ(1, gConstructions); EXPECT_EQ(1, gAlive); Spy::ClearActions(); - if (gDebug) { printf("%d - r12 = NS_NewRunnableMethodWithArgs(&TestByPointer, s)\n", __LINE__); } + if (gDebug) { printf("%d - r12 = NewRunnableMethod(&TestByPointer, s)\n", __LINE__); } nsCOMPtr r12 = - NS_NewRunnableMethodWithArgs( + NewRunnableMethod( rpt, &ThreadUtilsObject::TestByPointerToConst, &s); EXPECT_EQ(0, gAllConstructions); EXPECT_EQ(0, gDestructions); diff --git a/xpcom/tests/TestThreadUtils.cpp b/xpcom/tests/TestThreadUtils.cpp index e26f9d6821..5219338275 100644 --- a/xpcom/tests/TestThreadUtils.cpp +++ b/xpcom/tests/TestThreadUtils.cpp @@ -5,8 +5,11 @@ #include "TestHarness.h" #include "nsThreadUtils.h" +using namespace mozilla; + enum { TEST_CALL_VOID_ARG_VOID_RETURN, + TEST_CALL_VOID_ARG_VOID_RETURN_CONST, TEST_CALL_VOID_ARG_NONVOID_RETURN, TEST_CALL_NONVOID_ARG_VOID_RETURN, TEST_CALL_NONVOID_ARG_NONVOID_RETURN, @@ -63,6 +66,9 @@ public: void DoBar1(void) { gRunnableExecuted[TEST_CALL_VOID_ARG_VOID_RETURN] = true; } + void DoBar1Const(void) const { + gRunnableExecuted[TEST_CALL_VOID_ARG_VOID_RETURN_CONST] = true; + } nsresult DoBar2(void) { gRunnableExecuted[TEST_CALL_VOID_ARG_NONVOID_RETURN] = true; return NS_OK; @@ -120,6 +126,7 @@ int main(int argc, char** argv) { RefPtr foo = new nsFoo(); RefPtr bar = new nsBar(); + RefPtr constBar = bar; // This pointer will be freed at the end of the block // Do not dereference this pointer in the runnable method! @@ -128,23 +135,24 @@ int main(int argc, char** argv) // Read only string. Dereferencing in runnable method to check this works. char* message = (char*)"Test message"; - NS_DispatchToMainThread(NS_NewRunnableMethod(bar, &nsBar::DoBar1)); - NS_DispatchToMainThread(NS_NewRunnableMethod(bar, &nsBar::DoBar2)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg< RefPtr > + NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar1)); + NS_DispatchToMainThread(NewRunnableMethod(constBar, &nsBar::DoBar1Const)); + NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar2)); + NS_DispatchToMainThread(NewRunnableMethod> (bar, &nsBar::DoBar3, foo)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg< RefPtr > + NS_DispatchToMainThread(NewRunnableMethod> (bar, &nsBar::DoBar4, foo)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg(bar, &nsBar::DoBar5, rawFoo)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg(bar, &nsBar::DoBar6, message)); + NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar5, rawFoo)); + NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar6, message)); #ifdef HAVE_STDCALL - NS_DispatchToMainThread(NS_NewRunnableMethod(bar, &nsBar::DoBar1std)); - NS_DispatchToMainThread(NS_NewRunnableMethod(bar, &nsBar::DoBar2std)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg< RefPtr > + NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar1std)); + NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar2std)); + NS_DispatchToMainThread(NewRunnableMethod> (bar, &nsBar::DoBar3std, foo)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg< RefPtr > + NS_DispatchToMainThread(NewRunnableMethod> (bar, &nsBar::DoBar4std, foo)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg(bar, &nsBar::DoBar5std, rawFoo)); - NS_DispatchToMainThread(NS_NewRunnableMethodWithArg(bar, &nsBar::DoBar6std, message)); + NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar5std, rawFoo)); + NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar6std, message)); #endif } diff --git a/xpcom/threads/AbstractThread.cpp b/xpcom/threads/AbstractThread.cpp index 1aa8b4bd4d..a337efd9d4 100644 --- a/xpcom/threads/AbstractThread.cpp +++ b/xpcom/threads/AbstractThread.cpp @@ -88,7 +88,7 @@ public: if (!mTailDispatcher.isSome()) { mTailDispatcher.emplace(/* aIsTailDispatcher = */ true); - nsCOMPtr event = NS_NewRunnableMethod(this, &XPCOMThreadWrapper::FireTailDispatcher); + nsCOMPtr event = NewRunnableMethod(this, &XPCOMThreadWrapper::FireTailDispatcher); nsContentUtils::RunInStableState(event.forget()); } @@ -105,11 +105,19 @@ private: bool AbstractThread::RequiresTailDispatch(AbstractThread* aThread) const { + MOZ_ASSERT(aThread); // We require tail dispatch if both the source and destination // threads support it. return SupportsTailDispatch() && aThread->SupportsTailDispatch(); } +bool +AbstractThread::RequiresTailDispatchFromCurrentThread() const +{ + AbstractThread* current = GetCurrent(); + return current && RequiresTailDispatch(current); +} + AbstractThread* AbstractThread::MainThread() { diff --git a/xpcom/threads/AbstractThread.h b/xpcom/threads/AbstractThread.h index 7607ffd106..c3938ee29b 100644 --- a/xpcom/threads/AbstractThread.h +++ b/xpcom/threads/AbstractThread.h @@ -72,6 +72,7 @@ public: // Returns true if this thread requires all dispatches originating from // aThread go through the tail dispatcher. bool RequiresTailDispatch(AbstractThread* aThread) const; + bool RequiresTailDispatchFromCurrentThread() const; virtual TaskQueue* AsTaskQueue() { MOZ_CRASH("Not a task queue!"); } virtual nsIThread* AsXPCOMThread() { MOZ_CRASH("Not an XPCOM thread!"); } diff --git a/xpcom/threads/LazyIdleThread.cpp b/xpcom/threads/LazyIdleThread.cpp index 43d8857aa1..535a48a8e4 100644 --- a/xpcom/threads/LazyIdleThread.cpp +++ b/xpcom/threads/LazyIdleThread.cpp @@ -163,7 +163,7 @@ LazyIdleThread::EnsureThread() } nsCOMPtr runnable = - NS_NewRunnableMethod(this, &LazyIdleThread::InitThread); + NewRunnableMethod(this, &LazyIdleThread::InitThread); if (NS_WARN_IF(!runnable)) { return NS_ERROR_UNEXPECTED; } @@ -291,7 +291,7 @@ LazyIdleThread::ShutdownThread() #endif nsCOMPtr runnable = - NS_NewRunnableMethod(this, &LazyIdleThread::CleanupThread); + NewRunnableMethod(this, &LazyIdleThread::CleanupThread); if (NS_WARN_IF(!runnable)) { return NS_ERROR_UNEXPECTED; } @@ -368,7 +368,7 @@ LazyIdleThread::Release() mRefCnt = 1; nsCOMPtr runnable = - NS_NewNonOwningRunnableMethod(this, &LazyIdleThread::SelfDestruct); + NewNonOwningRunnableMethod(this, &LazyIdleThread::SelfDestruct); NS_WARN_IF_FALSE(runnable, "Couldn't make runnable!"); if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { @@ -561,7 +561,7 @@ LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */, if (shouldNotifyIdle) { nsCOMPtr runnable = - NS_NewRunnableMethod(this, &LazyIdleThread::ScheduleTimer); + NewRunnableMethod(this, &LazyIdleThread::ScheduleTimer); if (NS_WARN_IF(!runnable)) { return NS_ERROR_UNEXPECTED; } diff --git a/xpcom/threads/MozPromise.h b/xpcom/threads/MozPromise.h index 5e317312d2..21813b304b 100644 --- a/xpcom/threads/MozPromise.h +++ b/xpcom/threads/MozPromise.h @@ -792,7 +792,7 @@ public: // Provide a Monitor that should always be held when accessing this instance. void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; } - bool IsEmpty() + bool IsEmpty() const { if (mMonitor) { mMonitor->AssertCurrentThreadOwns(); diff --git a/xpcom/threads/SharedThreadPool.cpp b/xpcom/threads/SharedThreadPool.cpp index 4944e6fc68..004a352135 100644 --- a/xpcom/threads/SharedThreadPool.cpp +++ b/xpcom/threads/SharedThreadPool.cpp @@ -141,10 +141,9 @@ NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::Release(void) MOZ_ASSERT(!sPools->Get(mName)); // Dispatch an event to the main thread to call Shutdown() on - // the nsIThreadPool. The Runnable here will add a refcount to the pool, + // the nsIThreadPool. The Runnable here will add a refcount to the pool, // and when the Runnable releases the nsIThreadPool it will be deleted. - nsCOMPtr r = NS_NewRunnableMethod(mPool, &nsIThreadPool::Shutdown); - NS_DispatchToMainThread(r); + NS_DispatchToMainThread(NewRunnableMethod(mPool, &nsIThreadPool::Shutdown)); // Stabilize refcount, so that if something in the dtor QIs, it won't explode. mRefCnt = 1; diff --git a/xpcom/threads/StateMirroring.h b/xpcom/threads/StateMirroring.h index bcb65fea38..d004b6983a 100644 --- a/xpcom/threads/StateMirroring.h +++ b/xpcom/threads/StateMirroring.h @@ -152,9 +152,9 @@ private: { MIRROR_LOG("%s [%p] Disconnecting all mirrors", mName, this); for (size_t i = 0; i < mMirrors.Length(); ++i) { - nsCOMPtr r = - NS_NewRunnableMethod(mMirrors[i], &AbstractMirror::NotifyDisconnected); - mMirrors[i]->OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess); + mMirrors[i]->OwnerThread()->Dispatch(NewRunnableMethod(mMirrors[i], + &AbstractMirror::NotifyDisconnected), + AbstractThread::DontAssertDispatchSuccess); } mMirrors.Clear(); } @@ -191,8 +191,7 @@ private: // we can avoid sending multiple updates, and possibly avoid sending any // updates at all if the value ends up where it started. if (!alreadyNotifying) { - nsCOMPtr r = NS_NewRunnableMethod(this, &Impl::DoNotify); - AbstractThread::DispatchDirectTask(r.forget()); + AbstractThread::DispatchDirectTask(NewRunnableMethod(this, &Impl::DoNotify)); } } @@ -223,9 +222,7 @@ private: already_AddRefed MakeNotifier(AbstractMirror* aMirror) { - nsCOMPtr r = - NS_NewRunnableMethodWithArg(aMirror, &AbstractMirror::UpdateValue, mValue); - return r.forget(); + return NewRunnableMethod(aMirror, &AbstractMirror::UpdateValue, mValue);; } T mValue; @@ -328,7 +325,7 @@ private: MOZ_ASSERT(!IsConnected()); MOZ_ASSERT(OwnerThread()->RequiresTailDispatch(aCanonical->OwnerThread()), "Can't get coherency without tail dispatch"); - nsCOMPtr r = NS_NewRunnableMethodWithArg>> + nsCOMPtr r = NewRunnableMethod>> (aCanonical, &AbstractCanonical::AddMirror, this); aCanonical->OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess); mCanonical = aCanonical; @@ -343,7 +340,7 @@ private: } MIRROR_LOG("%s [%p] Disconnecting from %p", mName, this, mCanonical.get()); - nsCOMPtr r = NS_NewRunnableMethodWithArg>> + nsCOMPtr r = NewRunnableMethod>> (mCanonical, &AbstractCanonical::RemoveMirror, this); mCanonical->OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess); mCanonical = nullptr; diff --git a/xpcom/threads/StateWatching.h b/xpcom/threads/StateWatching.h index 3ad8c21840..a5543120b6 100644 --- a/xpcom/threads/StateWatching.h +++ b/xpcom/threads/StateWatching.h @@ -255,8 +255,7 @@ private: mStrongRef = mOwner; // Hold the owner alive while notifying. // Queue up our notification jobs to run in a stable state. - nsCOMPtr r = NS_NewRunnableMethod(this, &PerCallbackWatcher::DoNotify); - mOwnerThread->TailDispatcher().AddDirectTask(r.forget()); + mOwnerThread->TailDispatcher().AddDirectTask(NewRunnableMethod(this, &PerCallbackWatcher::DoNotify)); } bool CallbackMethodIs(CallbackMethod aMethod) const diff --git a/xpcom/threads/SyncRunnable.h b/xpcom/threads/SyncRunnable.h index 9c493d9dcc..7b77dcd8a5 100644 --- a/xpcom/threads/SyncRunnable.h +++ b/xpcom/threads/SyncRunnable.h @@ -8,6 +8,7 @@ #define mozilla_SyncRunnable_h #include "nsThreadUtils.h" +#include "mozilla/AbstractThread.h" #include "mozilla/Monitor.h" namespace mozilla { @@ -61,6 +62,24 @@ public: } } + void DispatchToThread(AbstractThread* aThread, bool aForceDispatch = false) + { + if (!aForceDispatch && aThread->IsCurrentThreadIn()) { + mRunnable->Run(); + return; + } + + // Check we don't have tail dispatching here. Otherwise we will deadlock + // ourself when spinning the loop below. + MOZ_ASSERT(!aThread->RequiresTailDispatchFromCurrentThread()); + + aThread->Dispatch(RefPtr(this).forget()); + mozilla::MonitorAutoLock lock(mMonitor); + while (!mDone) { + lock.Wait(); + } + } + static void DispatchToThread(nsIEventTarget* aThread, nsIRunnable* aRunnable, bool aForceDispatch = false) @@ -69,6 +88,14 @@ public: s->DispatchToThread(aThread, aForceDispatch); } + static void DispatchToThread(AbstractThread* aThread, + nsIRunnable* aRunnable, + bool aForceDispatch = false) + { + RefPtr s(new SyncRunnable(aRunnable)); + s->DispatchToThread(aThread, aForceDispatch); + } + protected: NS_IMETHODIMP Run() { diff --git a/xpcom/threads/nsProcessCommon.cpp b/xpcom/threads/nsProcessCommon.cpp index cc2938f635..90585f3267 100644 --- a/xpcom/threads/nsProcessCommon.cpp +++ b/xpcom/threads/nsProcessCommon.cpp @@ -301,9 +301,7 @@ nsProcess::Monitor(void* aArg) if (NS_IsMainThread()) { process->ProcessComplete(); } else { - nsCOMPtr event = - NS_NewRunnableMethod(process, &nsProcess::ProcessComplete); - NS_DispatchToMainThread(event); + NS_DispatchToMainThread(NewRunnableMethod(process, &nsProcess::ProcessComplete)); } } diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp index 8be613d578..0b4ac6047a 100644 --- a/xpcom/threads/nsThreadPool.cpp +++ b/xpcom/threads/nsThreadPool.cpp @@ -146,9 +146,8 @@ nsThreadPool::ShutdownThread(nsIThread* aThread) // shutdown requires this thread have an event loop (and it may not, see bug // 10204784). The simplest way to cover all cases is to asynchronously // shutdown aThread from the main thread. - nsCOMPtr r = NS_NewRunnableMethod(aThread, - &nsIThread::AsyncShutdown); - NS_DispatchToMainThread(r); + NS_DispatchToMainThread(NewRunnableMethod(aThread, + &nsIThread::AsyncShutdown)); } NS_IMETHODIMP